mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #14052 from jordan-woyak/DirectIOFile
Common and DiscIO: Introduce a thread safe DirectIOFile and make BlobReader implementations use it.
This commit is contained in:
@@ -255,15 +255,33 @@ T ExpandValue(T value, size_t left_shift_amount)
|
||||
(T(-ExtractBit<0>(value)) >> (BitSize<T>() - left_shift_amount));
|
||||
}
|
||||
|
||||
// Convert a contiguous range of a trivially-copyable type to a `span<const u8>`
|
||||
template <std::ranges::contiguous_range T>
|
||||
requires(std::is_trivially_copyable_v<std::ranges::range_value_t<T>>)
|
||||
constexpr auto AsU8Span(const T& range)
|
||||
{
|
||||
return std::span{reinterpret_cast<const u8*>(range.data()), range.size() * sizeof(*range.data())};
|
||||
}
|
||||
|
||||
// Convert a contiguous range of a non-const trivially-copyable type to a `span<u8>`
|
||||
template <std::ranges::contiguous_range T>
|
||||
requires(std::is_trivially_copyable_v<std::ranges::range_value_t<T>>)
|
||||
constexpr auto AsWritableU8Span(T& range)
|
||||
{
|
||||
return std::span{reinterpret_cast<u8*>(range.data()), range.size() * sizeof(*range.data())};
|
||||
}
|
||||
|
||||
// Convert a trivially-copyable object to a `span<const u8>`
|
||||
template <typename T>
|
||||
requires(std::is_trivially_copyable_v<T>)
|
||||
requires(!std::ranges::contiguous_range<T> && std::is_trivially_copyable_v<T>)
|
||||
constexpr auto AsU8Span(const T& obj)
|
||||
{
|
||||
return std::span{reinterpret_cast<const u8*>(std::addressof(obj)), sizeof(obj)};
|
||||
}
|
||||
|
||||
// Convert a non-const trivially-copyable object to a `span<u8>`
|
||||
template <typename T>
|
||||
requires(std::is_trivially_copyable_v<T>)
|
||||
requires(!std::ranges::contiguous_range<T> && std::is_trivially_copyable_v<T>)
|
||||
constexpr auto AsWritableU8Span(T& obj)
|
||||
{
|
||||
return std::span{reinterpret_cast<u8*>(std::addressof(obj)), sizeof(obj)};
|
||||
|
||||
@@ -52,6 +52,8 @@ add_library(common
|
||||
Debug/Threads.h
|
||||
Debug/Watches.cpp
|
||||
Debug/Watches.h
|
||||
DirectIOFile.cpp
|
||||
DirectIOFile.h
|
||||
DynamicLibrary.cpp
|
||||
DynamicLibrary.h
|
||||
ENet.cpp
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/DirectIOFile.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#else
|
||||
#include <string>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
|
||||
#include "Common/Lazy.h"
|
||||
#endif
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
#endif
|
||||
|
||||
#include "Common/Assert.h"
|
||||
|
||||
namespace File
|
||||
{
|
||||
DirectIOFile::DirectIOFile() = default;
|
||||
|
||||
DirectIOFile::~DirectIOFile()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
DirectIOFile::DirectIOFile(const DirectIOFile& other)
|
||||
{
|
||||
*this = other.Duplicate();
|
||||
}
|
||||
|
||||
DirectIOFile& DirectIOFile::operator=(const DirectIOFile& other)
|
||||
{
|
||||
return *this = other.Duplicate();
|
||||
}
|
||||
|
||||
DirectIOFile::DirectIOFile(DirectIOFile&& other)
|
||||
{
|
||||
Swap(other);
|
||||
}
|
||||
|
||||
DirectIOFile& DirectIOFile::operator=(DirectIOFile&& other)
|
||||
{
|
||||
Close();
|
||||
Swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DirectIOFile::DirectIOFile(const std::string& path, AccessMode access_mode, OpenMode open_mode)
|
||||
{
|
||||
Open(path, access_mode, open_mode);
|
||||
}
|
||||
|
||||
bool DirectIOFile::Open(const std::string& path, AccessMode access_mode, OpenMode open_mode)
|
||||
{
|
||||
ASSERT(!IsOpen());
|
||||
|
||||
if (open_mode == OpenMode::Default)
|
||||
open_mode = (access_mode == AccessMode::Write) ? OpenMode::Truncate : OpenMode::Existing;
|
||||
|
||||
// This is not a sensible combination. Fail here to not rely on OS-specific behaviors.
|
||||
if (access_mode == AccessMode::Read && open_mode == OpenMode::Truncate)
|
||||
return false;
|
||||
|
||||
#if defined(_WIN32)
|
||||
DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
|
||||
if (access_mode == AccessMode::Read)
|
||||
desired_access = GENERIC_READ;
|
||||
else if (access_mode == AccessMode::Write)
|
||||
desired_access = GENERIC_WRITE;
|
||||
|
||||
// Allow deleting and renaming through our handle.
|
||||
desired_access |= DELETE;
|
||||
|
||||
// All sharing is allowed to more closely match default behavior on other OSes.
|
||||
constexpr DWORD share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
|
||||
DWORD creation_disposition = OPEN_ALWAYS;
|
||||
if (open_mode == OpenMode::Truncate)
|
||||
creation_disposition = CREATE_ALWAYS;
|
||||
else if (open_mode == OpenMode::Create)
|
||||
creation_disposition = CREATE_NEW;
|
||||
else if (open_mode == OpenMode::Existing)
|
||||
creation_disposition = OPEN_EXISTING;
|
||||
|
||||
m_handle = CreateFile(UTF8ToTStr(path).c_str(), desired_access, share_mode, nullptr,
|
||||
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (!IsOpen())
|
||||
WARN_LOG_FMT(COMMON, "CreateFile: {}", Common::GetLastErrorString());
|
||||
|
||||
#else
|
||||
#if defined(ANDROID)
|
||||
if (IsPathAndroidContent(path))
|
||||
{
|
||||
// Android documentation says that "w" may or may not truncate.
|
||||
// In case it does, we'll use "rw" when we don't want truncation.
|
||||
// This wrongly enables read access when we don't need it.
|
||||
if (access_mode == AccessMode::Write && open_mode != OpenMode::Truncate)
|
||||
access_mode = AccessMode::ReadAndWrite;
|
||||
|
||||
std::string open_mode_str = "rw";
|
||||
if (access_mode == AccessMode::Read)
|
||||
open_mode_str = "r";
|
||||
else if (access_mode == AccessMode::Write)
|
||||
open_mode_str = "w";
|
||||
|
||||
// FYI: File::Exists can be slow on Android.
|
||||
Common::Lazy<bool> file_exists{[&] { return Exists(path); }};
|
||||
|
||||
// A few features are emulated in a non-atomic manner.
|
||||
if (open_mode == OpenMode::Existing)
|
||||
{
|
||||
if (access_mode != AccessMode::Read && !*file_exists)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (open_mode == OpenMode::Truncate)
|
||||
open_mode_str += 't';
|
||||
else if (open_mode == OpenMode::Create && *file_exists)
|
||||
return false;
|
||||
|
||||
// Modes other than `Existing` may create a file, but "r" won't do that automatically.
|
||||
if (access_mode == AccessMode::Read && !*file_exists)
|
||||
CreateEmptyFile(path);
|
||||
}
|
||||
|
||||
m_fd = OpenAndroidContent(path, open_mode_str);
|
||||
|
||||
return IsOpen();
|
||||
}
|
||||
#endif
|
||||
int flags = O_RDWR;
|
||||
if (access_mode == AccessMode::Read)
|
||||
flags = O_RDONLY;
|
||||
else if (access_mode == AccessMode::Write)
|
||||
flags = O_WRONLY;
|
||||
|
||||
if (open_mode == OpenMode::Truncate)
|
||||
flags |= O_CREAT | O_TRUNC;
|
||||
else if (open_mode == OpenMode::Create)
|
||||
flags |= O_CREAT | O_EXCL;
|
||||
else if (open_mode != OpenMode::Existing)
|
||||
flags |= O_CREAT;
|
||||
|
||||
m_fd = open(path.c_str(), flags, 0666);
|
||||
|
||||
#endif
|
||||
|
||||
return IsOpen();
|
||||
}
|
||||
|
||||
bool DirectIOFile::Close()
|
||||
{
|
||||
if (!IsOpen())
|
||||
return false;
|
||||
|
||||
m_current_offset = 0;
|
||||
|
||||
#if defined(_WIN32)
|
||||
return CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE)) != 0;
|
||||
#else
|
||||
return close(std::exchange(m_fd, -1)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DirectIOFile::IsOpen() const
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return m_handle != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return m_fd != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
template <auto* TransferFunc>
|
||||
static bool OverlappedTransfer(HANDLE handle, u64 offset, auto* data_ptr, u64 size)
|
||||
{
|
||||
// ReadFile/WriteFile take a 32bit size so we must loop to handle our 64bit size.
|
||||
while (true)
|
||||
{
|
||||
OVERLAPPED overlapped{};
|
||||
overlapped.Offset = DWORD(offset);
|
||||
overlapped.OffsetHigh = DWORD(offset >> 32);
|
||||
|
||||
DWORD bytes_transferred{};
|
||||
if (TransferFunc(handle, data_ptr, MathUtil::SaturatingCast<DWORD>(size), &bytes_transferred,
|
||||
&overlapped) == 0)
|
||||
{
|
||||
ERROR_LOG_FMT(COMMON, "OverlappedTransfer: {}", Common::GetLastErrorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
size -= bytes_transferred;
|
||||
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
offset += bytes_transferred;
|
||||
data_ptr += bytes_transferred;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool DirectIOFile::OffsetRead(u64 offset, u8* out_ptr, u64 size)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return OverlappedTransfer<ReadFile>(m_handle, offset, out_ptr, size);
|
||||
#else
|
||||
return pread(m_fd, out_ptr, size, off_t(offset)) == ssize_t(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DirectIOFile::OffsetWrite(u64 offset, const u8* in_ptr, u64 size)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return OverlappedTransfer<WriteFile>(m_handle, offset, in_ptr, size);
|
||||
#else
|
||||
return pwrite(m_fd, in_ptr, size, off_t(offset)) == ssize_t(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 DirectIOFile::GetSize() const
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
LARGE_INTEGER result{};
|
||||
if (GetFileSizeEx(m_handle, &result) != 0)
|
||||
return result.QuadPart;
|
||||
#else
|
||||
struct stat st{};
|
||||
if (fstat(m_fd, &st) == 0)
|
||||
return st.st_size;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DirectIOFile::Seek(s64 offset, SeekOrigin origin)
|
||||
{
|
||||
if (!IsOpen())
|
||||
return false;
|
||||
|
||||
u64 reference_pos = 0;
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin::Current:
|
||||
reference_pos = m_current_offset;
|
||||
break;
|
||||
case SeekOrigin::End:
|
||||
reference_pos = GetSize();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't let our current offset underflow.
|
||||
if (offset < 0 && u64(-offset) > reference_pos)
|
||||
return false;
|
||||
|
||||
m_current_offset = reference_pos + offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DirectIOFile::Flush()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return FlushFileBuffers(m_handle) != 0;
|
||||
#else
|
||||
return fsync(m_fd) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DirectIOFile::Swap(DirectIOFile& other)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
std::swap(m_handle, other.m_handle);
|
||||
#else
|
||||
std::swap(m_fd, other.m_fd);
|
||||
#endif
|
||||
std::swap(m_current_offset, other.m_current_offset);
|
||||
}
|
||||
|
||||
DirectIOFile DirectIOFile::Duplicate() const
|
||||
{
|
||||
DirectIOFile result;
|
||||
|
||||
if (!IsOpen())
|
||||
return result;
|
||||
|
||||
#if defined(_WIN32)
|
||||
const auto current_process = GetCurrentProcess();
|
||||
if (DuplicateHandle(current_process, m_handle, current_process, &result.m_handle, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS) == 0)
|
||||
{
|
||||
ERROR_LOG_FMT(COMMON, "DuplicateHandle: {}", Common::GetLastErrorString());
|
||||
}
|
||||
#else
|
||||
result.m_fd = dup(m_fd);
|
||||
#endif
|
||||
|
||||
ASSERT(result.IsOpen());
|
||||
|
||||
result.m_current_offset = m_current_offset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Resize(DirectIOFile& file, u64 size)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// This operation is not "atomic", but it's the only thing we're using the file pointer for.
|
||||
// Concurrent `Resize` would need some external synchronization to prevent race regardless.
|
||||
const LARGE_INTEGER distance{.QuadPart = LONGLONG(size)};
|
||||
return (SetFilePointerEx(file.GetHandle(), distance, nullptr, FILE_BEGIN) != 0) &&
|
||||
(SetEndOfFile(file.GetHandle()) != 0);
|
||||
#else
|
||||
return ftruncate(file.GetHandle(), off_t(size)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Rename(DirectIOFile& file, const std::string& source_path [[maybe_unused]],
|
||||
const std::string& destination_path)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
const auto dest_name = UTF8ToWString(destination_path);
|
||||
const auto dest_name_byte_size = DWORD(dest_name.size() * sizeof(WCHAR));
|
||||
FILE_RENAME_INFO info{
|
||||
.ReplaceIfExists = TRUE,
|
||||
.FileNameLength = dest_name_byte_size, // The size in bytes, not including null termination.
|
||||
};
|
||||
constexpr auto filename_struct_offset = offsetof(FILE_RENAME_INFO, FileName);
|
||||
Common::UniqueBuffer<u8> buffer(filename_struct_offset + dest_name_byte_size + sizeof(WCHAR));
|
||||
std::memcpy(buffer.data(), &info, filename_struct_offset);
|
||||
std::memcpy(buffer.data() + filename_struct_offset, dest_name.c_str(),
|
||||
dest_name_byte_size + sizeof(WCHAR));
|
||||
return SetFileInformationByHandle(file.GetHandle(), FileRenameInfo, buffer.data(),
|
||||
DWORD(buffer.size())) != 0;
|
||||
#else
|
||||
return file.IsOpen() && Rename(source_path, destination_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Delete(DirectIOFile& file, const std::string& filename)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
FILE_DISPOSITION_INFO info{.DeleteFile = TRUE};
|
||||
return SetFileInformationByHandle(file.GetHandle(), FileDispositionInfo, &info, sizeof(info)) !=
|
||||
0;
|
||||
#else
|
||||
return file.IsOpen() && Delete(filename, IfAbsentBehavior::NoConsoleWarning);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace File
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
|
||||
namespace File
|
||||
{
|
||||
enum class AccessMode
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
ReadAndWrite,
|
||||
};
|
||||
|
||||
enum class OpenMode
|
||||
{
|
||||
// Based on the provided `AccessMode`.
|
||||
// Read: Existing.
|
||||
// Write: Truncate.
|
||||
// ReadAndWrite: Existing.
|
||||
Default,
|
||||
|
||||
// Either create a new file or open an existing file.
|
||||
Always,
|
||||
|
||||
// Like `Always`, but also erase the contents of an existing file.
|
||||
Truncate,
|
||||
|
||||
// Require a file to already exist. Fail otherwise.
|
||||
Existing,
|
||||
|
||||
// Require a new file be created. Fail if one already exists.
|
||||
Create,
|
||||
};
|
||||
|
||||
// This file wrapper avoids use of the underlying system file position.
|
||||
// It keeps track of its own file position and read/write calls directly use it.
|
||||
// This makes copied handles entirely thread safe.
|
||||
class DirectIOFile final
|
||||
{
|
||||
public:
|
||||
DirectIOFile();
|
||||
~DirectIOFile();
|
||||
|
||||
// Copies and moves are allowed.
|
||||
DirectIOFile(const DirectIOFile&);
|
||||
DirectIOFile& operator=(const DirectIOFile&);
|
||||
DirectIOFile(DirectIOFile&&);
|
||||
DirectIOFile& operator=(DirectIOFile&&);
|
||||
|
||||
explicit DirectIOFile(const std::string& path, AccessMode access_mode,
|
||||
OpenMode open_mode = OpenMode::Default);
|
||||
|
||||
bool Open(const std::string& path, AccessMode access_mode,
|
||||
OpenMode open_mode = OpenMode::Default);
|
||||
|
||||
bool Close();
|
||||
|
||||
bool IsOpen() const;
|
||||
|
||||
// An offset from the start of the file may be specified directly.
|
||||
// These explicit offset versions entirely ignore the current file position.
|
||||
// They are thread safe, even when used on the same object.
|
||||
|
||||
bool OffsetRead(u64 offset, u8* out_ptr, u64 size);
|
||||
bool OffsetRead(u64 offset, std::span<u8> out_data)
|
||||
{
|
||||
return OffsetRead(offset, out_data.data(), out_data.size());
|
||||
}
|
||||
bool OffsetWrite(u64 offset, const u8* in_ptr, u64 size);
|
||||
bool OffsetWrite(auto offset, std::span<const u8> in_data)
|
||||
{
|
||||
return OffsetWrite(offset, in_data.data(), in_data.size());
|
||||
}
|
||||
|
||||
// These Read/Write functions advance the current position on success.
|
||||
|
||||
bool Read(u8* out_ptr, u64 size)
|
||||
{
|
||||
if (!OffsetRead(m_current_offset, out_ptr, size))
|
||||
return false;
|
||||
m_current_offset += size;
|
||||
return true;
|
||||
}
|
||||
bool Read(std::span<u8> out_data) { return Read(out_data.data(), out_data.size()); }
|
||||
|
||||
bool Write(const u8* in_ptr, u64 size)
|
||||
{
|
||||
if (!OffsetWrite(m_current_offset, in_ptr, size))
|
||||
return false;
|
||||
m_current_offset += size;
|
||||
return true;
|
||||
}
|
||||
bool Write(std::span<const u8> in_data) { return Write(in_data.data(), in_data.size()); }
|
||||
|
||||
// Returns 0 on error.
|
||||
u64 GetSize() const;
|
||||
|
||||
bool Seek(s64 offset, SeekOrigin origin);
|
||||
|
||||
// Returns 0 when not open.
|
||||
u64 Tell() const { return m_current_offset; }
|
||||
|
||||
bool Flush();
|
||||
|
||||
auto GetHandle() const
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return m_handle;
|
||||
#else
|
||||
return m_fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void Swap(DirectIOFile& other);
|
||||
DirectIOFile Duplicate() const;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// A workaround to avoid including <windows.h> in this header.
|
||||
// HANDLE is just void* and INVALID_HANDLE_VALUE is -1.
|
||||
using HandleType = void*;
|
||||
HandleType m_handle{HandleType(-1)};
|
||||
#else
|
||||
using HandleType = int;
|
||||
HandleType m_fd{-1};
|
||||
#endif
|
||||
|
||||
u64 m_current_offset{};
|
||||
};
|
||||
|
||||
// These take an open file handle to avoid failures from other processes trying to open our files.
|
||||
// This is mainly an issue on Windows.
|
||||
|
||||
bool Resize(DirectIOFile& file, u64 size);
|
||||
|
||||
// Attempts to replace a destination file if one already exists.
|
||||
// Note: Windows uses `file`. Elsewhere uses `source_path`, but `file` is checked for openness.
|
||||
bool Rename(DirectIOFile& file, const std::string& source_path,
|
||||
const std::string& destination_path);
|
||||
// Note: Ditto, only Windows actually uses the file handle. Provide both.
|
||||
bool Delete(DirectIOFile& file, const std::string& filename);
|
||||
|
||||
} // namespace File
|
||||
@@ -101,15 +101,6 @@ bool IOFile::Close()
|
||||
return m_good;
|
||||
}
|
||||
|
||||
IOFile IOFile::Duplicate(const char openmode[]) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return IOFile(_fdopen(_dup(_fileno(m_file)), openmode));
|
||||
#else // _WIN32
|
||||
return IOFile(fdopen(dup(fileno(m_file)), openmode));
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
void IOFile::SetHandle(std::FILE* file)
|
||||
{
|
||||
Close();
|
||||
|
||||
@@ -51,8 +51,6 @@ public:
|
||||
SharedAccess sh = SharedAccess::Default);
|
||||
bool Close();
|
||||
|
||||
IOFile Duplicate(const char openmode[]) const;
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_trivially_copyable_v<T>)
|
||||
bool ReadArray(T* elements, size_t count, size_t* num_read = nullptr)
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "DiscIO/CISOBlob.h"
|
||||
@@ -213,9 +212,9 @@ u32 SectorReader::ReadChunk(u8* buffer, u64 chunk_num)
|
||||
|
||||
std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename)
|
||||
{
|
||||
File::IOFile file(filename, "rb");
|
||||
File::DirectIOFile file(filename, File::AccessMode::Read);
|
||||
u32 magic;
|
||||
if (!file.ReadArray(&magic, 1))
|
||||
if (!file.Read(Common::AsWritableU8Span(magic)))
|
||||
return nullptr;
|
||||
|
||||
// Conveniently, every supported file format (except for plain disc images and
|
||||
|
||||
@@ -4,22 +4,20 @@
|
||||
#include "DiscIO/CISOBlob.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
CISOFileReader::CISOFileReader(File::IOFile file) : m_file(std::move(file))
|
||||
CISOFileReader::CISOFileReader(File::DirectIOFile file) : m_file(std::move(file))
|
||||
{
|
||||
m_size = m_file.GetSize();
|
||||
|
||||
CISOHeader header;
|
||||
m_file.Seek(0, File::SeekOrigin::Begin);
|
||||
m_file.ReadArray(&header, 1);
|
||||
m_file.OffsetRead(0, Common::AsWritableU8Span(header));
|
||||
|
||||
m_block_size = header.block_size;
|
||||
|
||||
@@ -28,11 +26,10 @@ CISOFileReader::CISOFileReader(File::IOFile file) : m_file(std::move(file))
|
||||
m_ciso_map[idx] = (1 == header.map[idx]) ? count++ : UNUSED_BLOCK_ID;
|
||||
}
|
||||
|
||||
std::unique_ptr<CISOFileReader> CISOFileReader::Create(File::IOFile file)
|
||||
std::unique_ptr<CISOFileReader> CISOFileReader::Create(File::DirectIOFile file)
|
||||
{
|
||||
CISOHeader header;
|
||||
if (file.Seek(0, File::SeekOrigin::Begin) && file.ReadArray(&header, 1) &&
|
||||
header.magic == CISO_MAGIC)
|
||||
if (file.OffsetRead(0, Common::AsWritableU8Span(header)) && header.magic == CISO_MAGIC)
|
||||
{
|
||||
return std::unique_ptr<CISOFileReader>(new CISOFileReader(std::move(file)));
|
||||
}
|
||||
@@ -42,7 +39,7 @@ std::unique_ptr<CISOFileReader> CISOFileReader::Create(File::IOFile file)
|
||||
|
||||
std::unique_ptr<BlobReader> CISOFileReader::CopyReader() const
|
||||
{
|
||||
return Create(m_file.Duplicate("rb"));
|
||||
return Create(m_file);
|
||||
}
|
||||
|
||||
u64 CISOFileReader::GetDataSize() const
|
||||
@@ -71,12 +68,8 @@ bool CISOFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
// calculate the base address
|
||||
u64 const file_off = CISO_HEADER_SIZE + m_ciso_map[block] * (u64)m_block_size + data_offset;
|
||||
|
||||
if (!(m_file.Seek(file_off, File::SeekOrigin::Begin) &&
|
||||
m_file.ReadArray(out_ptr, bytes_to_read)))
|
||||
{
|
||||
m_file.ClearError();
|
||||
if (!m_file.OffsetRead(file_off, out_ptr, bytes_to_read))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace DiscIO
|
||||
@@ -33,7 +32,7 @@ struct CISOHeader
|
||||
class CISOFileReader final : public BlobReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<CISOFileReader> Create(File::IOFile file);
|
||||
static std::unique_ptr<CISOFileReader> Create(File::DirectIOFile file);
|
||||
|
||||
BlobType GetBlobType() const override { return BlobType::CISO; }
|
||||
std::unique_ptr<BlobReader> CopyReader() const override;
|
||||
@@ -50,12 +49,12 @@ public:
|
||||
bool Read(u64 offset, u64 nbytes, u8* out_ptr) override;
|
||||
|
||||
private:
|
||||
CISOFileReader(File::IOFile file);
|
||||
CISOFileReader(File::DirectIOFile file);
|
||||
|
||||
typedef u16 MapType;
|
||||
static constexpr MapType UNUSED_BLOCK_ID = UINT16_MAX;
|
||||
|
||||
File::IOFile m_file;
|
||||
File::DirectIOFile m_file;
|
||||
u64 m_size;
|
||||
u32 m_block_size;
|
||||
MapType m_ciso_map[CISO_MAP_SIZE];
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "DiscIO/CompressedBlob.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -19,10 +18,10 @@
|
||||
#endif
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Hash.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
@@ -32,22 +31,23 @@
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
bool IsGCZBlob(File::IOFile& file);
|
||||
bool IsGCZBlob(File::DirectIOFile& file);
|
||||
|
||||
CompressedBlobReader::CompressedBlobReader(File::IOFile file, const std::string& filename)
|
||||
CompressedBlobReader::CompressedBlobReader(File::DirectIOFile file, const std::string& filename)
|
||||
: m_file(std::move(file)), m_file_name(filename)
|
||||
{
|
||||
m_file_size = m_file.GetSize();
|
||||
m_file.Seek(0, File::SeekOrigin::Begin);
|
||||
m_file.ReadArray(&m_header, 1);
|
||||
m_file.Read(Common::AsWritableU8Span(m_header));
|
||||
|
||||
SetSectorSize(m_header.block_size);
|
||||
|
||||
// cache block pointers and hashes
|
||||
m_block_pointers.resize(m_header.num_blocks);
|
||||
m_file.ReadArray(m_block_pointers.data(), m_header.num_blocks);
|
||||
m_file.Read(Common::AsWritableU8Span(m_block_pointers));
|
||||
|
||||
m_hashes.resize(m_header.num_blocks);
|
||||
m_file.ReadArray(m_hashes.data(), m_header.num_blocks);
|
||||
m_file.Read(Common::AsWritableU8Span(m_hashes));
|
||||
|
||||
m_data_offset = (sizeof(CompressedBlobHeader)) +
|
||||
(sizeof(u64)) * m_header.num_blocks // skip block pointers
|
||||
@@ -60,7 +60,7 @@ CompressedBlobReader::CompressedBlobReader(File::IOFile file, const std::string&
|
||||
m_zlib_buffer.resize(zlib_buffer_size);
|
||||
}
|
||||
|
||||
std::unique_ptr<CompressedBlobReader> CompressedBlobReader::Create(File::IOFile file,
|
||||
std::unique_ptr<CompressedBlobReader> CompressedBlobReader::Create(File::DirectIOFile file,
|
||||
const std::string& filename)
|
||||
{
|
||||
if (IsGCZBlob(file))
|
||||
@@ -74,7 +74,7 @@ CompressedBlobReader::~CompressedBlobReader() = default;
|
||||
|
||||
std::unique_ptr<BlobReader> CompressedBlobReader::CopyReader() const
|
||||
{
|
||||
return Create(m_file.Duplicate("rb"), m_file_name);
|
||||
return Create(m_file, m_file_name);
|
||||
}
|
||||
|
||||
// IMPORTANT: Calling this function invalidates all earlier pointers gotten from this function.
|
||||
@@ -107,12 +107,10 @@ bool CompressedBlobReader::GetBlock(u64 block_num, u8* out_ptr)
|
||||
// clear unused part of zlib buffer. maybe this can be deleted when it works fully.
|
||||
memset(&m_zlib_buffer[comp_block_size], 0, m_zlib_buffer.size() - comp_block_size);
|
||||
|
||||
m_file.Seek(offset, File::SeekOrigin::Begin);
|
||||
if (!m_file.ReadBytes(m_zlib_buffer.data(), comp_block_size))
|
||||
if (!m_file.OffsetRead(offset, m_zlib_buffer.data(), comp_block_size))
|
||||
{
|
||||
ERROR_LOG_FMT(DISCIO, "The disc image \"{}\" is truncated, some of the data is missing.",
|
||||
m_file_name);
|
||||
m_file.ClearError();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -241,7 +239,7 @@ static ConversionResult<OutputParameters> Compress(CompressThreadState* state,
|
||||
return std::move(output_parameters);
|
||||
}
|
||||
|
||||
static ConversionResultCode Output(OutputParameters parameters, File::IOFile* outfile,
|
||||
static ConversionResultCode Output(OutputParameters parameters, File::DirectIOFile* outfile,
|
||||
u64* position, std::vector<u64>* offsets, int progress_monitor,
|
||||
u32 num_blocks, const CompressCB& callback)
|
||||
{
|
||||
@@ -252,7 +250,7 @@ static ConversionResultCode Output(OutputParameters parameters, File::IOFile* ou
|
||||
|
||||
*position += parameters.data.size();
|
||||
|
||||
if (!outfile->WriteBytes(parameters.data.data(), parameters.data.size()))
|
||||
if (!outfile->Write(parameters.data))
|
||||
return ConversionResultCode::WriteFailed;
|
||||
|
||||
if (parameters.block_number % progress_monitor == 0)
|
||||
@@ -278,8 +276,8 @@ bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path,
|
||||
{
|
||||
ASSERT(infile->GetDataSizeType() == DataSizeType::Accurate);
|
||||
|
||||
File::IOFile outfile(outfile_path, "wb");
|
||||
if (!outfile)
|
||||
File::DirectIOFile outfile(outfile_path, File::AccessMode::Write);
|
||||
if (!outfile.IsOpen())
|
||||
{
|
||||
PanicAlertFmtT(
|
||||
"Failed to open the output file \"{0}\".\n"
|
||||
@@ -365,9 +363,9 @@ bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path,
|
||||
{
|
||||
// Okay, go back and fill in headers
|
||||
outfile.Seek(0, File::SeekOrigin::Begin);
|
||||
outfile.WriteArray(&header, 1);
|
||||
outfile.WriteArray(offsets.data(), header.num_blocks);
|
||||
outfile.WriteArray(hashes.data(), header.num_blocks);
|
||||
outfile.Write(Common::AsU8Span(header));
|
||||
outfile.Write(Common::AsU8Span(offsets));
|
||||
outfile.Write(Common::AsU8Span(hashes));
|
||||
|
||||
callback(Common::GetStringT("Done compressing disc image."), 1.0f);
|
||||
}
|
||||
@@ -385,15 +383,10 @@ bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path,
|
||||
return result == ConversionResultCode::Success;
|
||||
}
|
||||
|
||||
bool IsGCZBlob(File::IOFile& file)
|
||||
bool IsGCZBlob(File::DirectIOFile& file)
|
||||
{
|
||||
const u64 position = file.Tell();
|
||||
if (!file.Seek(0, File::SeekOrigin::Begin))
|
||||
return false;
|
||||
CompressedBlobHeader header;
|
||||
bool is_gcz = file.ReadArray(&header, 1) && header.magic_cookie == GCZ_MAGIC;
|
||||
file.Seek(position, File::SeekOrigin::Begin);
|
||||
return is_gcz;
|
||||
return file.OffsetRead(0, Common::AsWritableU8Span(header)) && header.magic_cookie == GCZ_MAGIC;
|
||||
}
|
||||
|
||||
} // namespace DiscIO
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace DiscIO
|
||||
@@ -43,7 +43,7 @@ struct CompressedBlobHeader // 32 bytes
|
||||
class CompressedBlobReader final : public SectorReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<CompressedBlobReader> Create(File::IOFile file,
|
||||
static std::unique_ptr<CompressedBlobReader> Create(File::DirectIOFile file,
|
||||
const std::string& filename);
|
||||
~CompressedBlobReader() override;
|
||||
|
||||
@@ -65,13 +65,13 @@ public:
|
||||
bool GetBlock(u64 block_num, u8* out_ptr) override;
|
||||
|
||||
private:
|
||||
CompressedBlobReader(File::IOFile file, const std::string& filename);
|
||||
CompressedBlobReader(File::DirectIOFile file, const std::string& filename);
|
||||
|
||||
CompressedBlobHeader m_header;
|
||||
std::vector<u64> m_block_pointers;
|
||||
std::vector<u32> m_hashes;
|
||||
int m_data_offset;
|
||||
File::IOFile m_file;
|
||||
File::DirectIOFile m_file;
|
||||
u64 m_file_size;
|
||||
std::vector<u8> m_zlib_buffer;
|
||||
std::string m_file_name;
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
#include "DiscIO/DirectoryBlob.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@@ -17,14 +15,12 @@
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/Boot/DolReader.h"
|
||||
#include "Core/IOS/ES/Formats.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/DiscUtils.h"
|
||||
@@ -97,9 +93,8 @@ bool DiscContent::Read(u64* offset, u64* length, u8** buffer, DirectoryBlobReade
|
||||
if (std::holds_alternative<ContentFile>(m_content_source))
|
||||
{
|
||||
const auto& content = std::get<ContentFile>(m_content_source);
|
||||
File::IOFile file(content.m_filename, "rb");
|
||||
if (!file.Seek(content.m_offset + offset_in_content, File::SeekOrigin::Begin) ||
|
||||
!file.ReadBytes(*buffer, bytes_to_read))
|
||||
File::DirectIOFile file(content.m_filename, File::AccessMode::Read);
|
||||
if (!file.OffsetRead(content.m_offset + offset_in_content, *buffer, bytes_to_read))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1013,9 +1008,9 @@ void DirectoryBlobPartition::SetBI2(std::vector<u8> bi2)
|
||||
|
||||
u64 DirectoryBlobPartition::SetApploaderFromFile(const std::string& path)
|
||||
{
|
||||
File::IOFile file(path, "rb");
|
||||
File::DirectIOFile file(path, File::AccessMode::Read);
|
||||
std::vector<u8> apploader(file.GetSize());
|
||||
file.ReadBytes(apploader.data(), apploader.size());
|
||||
file.Read(apploader);
|
||||
return SetApploader(std::move(apploader), path);
|
||||
}
|
||||
|
||||
@@ -1256,10 +1251,9 @@ void DirectoryBlobPartition::WriteDirectory(std::vector<u8>* fst_data,
|
||||
|
||||
static size_t ReadFileToVector(const std::string& path, std::vector<u8>* vector)
|
||||
{
|
||||
File::IOFile file(path, "rb");
|
||||
size_t bytes_read;
|
||||
file.ReadArray<u8>(vector->data(), std::min<u64>(file.GetSize(), vector->size()), &bytes_read);
|
||||
return bytes_read;
|
||||
File::DirectIOFile file(path, File::AccessMode::Read);
|
||||
file.Read(vector->data(), std::min<u64>(file.GetSize(), vector->size()));
|
||||
return file.Tell();
|
||||
}
|
||||
|
||||
static void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer)
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "DiscIO/WiiEncryptionCache.h"
|
||||
@@ -23,7 +22,6 @@
|
||||
namespace File
|
||||
{
|
||||
struct FSTEntry;
|
||||
class IOFile;
|
||||
} // namespace File
|
||||
|
||||
namespace DiscIO
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
PlainFileReader::PlainFileReader(File::IOFile file) : m_file(std::move(file))
|
||||
PlainFileReader::PlainFileReader(File::DirectIOFile file) : m_file(std::move(file))
|
||||
{
|
||||
m_size = m_file.GetSize();
|
||||
}
|
||||
|
||||
std::unique_ptr<PlainFileReader> PlainFileReader::Create(File::IOFile file)
|
||||
std::unique_ptr<PlainFileReader> PlainFileReader::Create(File::DirectIOFile file)
|
||||
{
|
||||
if (file)
|
||||
if (file.IsOpen())
|
||||
return std::unique_ptr<PlainFileReader>(new PlainFileReader(std::move(file)));
|
||||
|
||||
return nullptr;
|
||||
@@ -30,20 +30,12 @@ std::unique_ptr<PlainFileReader> PlainFileReader::Create(File::IOFile file)
|
||||
|
||||
std::unique_ptr<BlobReader> PlainFileReader::CopyReader() const
|
||||
{
|
||||
return Create(m_file.Duplicate("rb"));
|
||||
return Create(m_file);
|
||||
}
|
||||
|
||||
bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
{
|
||||
if (m_file.Seek(offset, File::SeekOrigin::Begin) && m_file.ReadBytes(out_ptr, nbytes))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_file.ClearError();
|
||||
return false;
|
||||
}
|
||||
return m_file.OffsetRead(offset, out_ptr, nbytes);
|
||||
}
|
||||
|
||||
bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
|
||||
@@ -51,8 +43,8 @@ bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
|
||||
{
|
||||
ASSERT(infile->GetDataSizeType() == DataSizeType::Accurate);
|
||||
|
||||
File::IOFile outfile(outfile_path, "wb");
|
||||
if (!outfile)
|
||||
File::DirectIOFile outfile(outfile_path, File::AccessMode::Write);
|
||||
if (!outfile.IsOpen())
|
||||
{
|
||||
PanicAlertFmtT(
|
||||
"Failed to open the output file \"{0}\".\n"
|
||||
@@ -99,7 +91,7 @@ bool ConvertToPlain(BlobReader* infile, const std::string& infile_path,
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
if (!outfile.WriteBytes(buffer.data(), sz))
|
||||
if (!outfile.Write(buffer.data(), sz))
|
||||
{
|
||||
PanicAlertFmtT("Failed to write the output file \"{0}\".\n"
|
||||
"Check that you have enough space available on the target drive.",
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace DiscIO
|
||||
@@ -16,7 +15,7 @@ namespace DiscIO
|
||||
class PlainFileReader final : public BlobReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<PlainFileReader> Create(File::IOFile file);
|
||||
static std::unique_ptr<PlainFileReader> Create(File::DirectIOFile file);
|
||||
|
||||
BlobType GetBlobType() const override { return BlobType::PLAIN; }
|
||||
std::unique_ptr<BlobReader> CopyReader() const override;
|
||||
@@ -33,9 +32,9 @@ public:
|
||||
bool Read(u64 offset, u64 nbytes, u8* out_ptr) override;
|
||||
|
||||
private:
|
||||
PlainFileReader(File::IOFile file);
|
||||
PlainFileReader(File::DirectIOFile file);
|
||||
|
||||
File::IOFile m_file;
|
||||
File::DirectIOFile m_file;
|
||||
u64 m_size;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Crypto/AES.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Swap.h"
|
||||
@@ -39,8 +39,8 @@ bool NFSFileReader::ReadKey(const std::string& path, const std::string& director
|
||||
}
|
||||
|
||||
const std::string key_path = parent + "code/htk.bin";
|
||||
File::IOFile key_file(key_path, "rb");
|
||||
if (!key_file.ReadBytes(key_out->data(), key_out->size()))
|
||||
File::DirectIOFile key_file(key_path, File::AccessMode::Read);
|
||||
if (!key_file.Read(*key_out))
|
||||
{
|
||||
ERROR_LOG_FMT(DISCIO, "Failed to read from {}", key_path);
|
||||
return false;
|
||||
@@ -67,13 +67,13 @@ std::vector<NFSLBARange> NFSFileReader::GetLBARanges(const NFSHeader& header)
|
||||
return lba_ranges;
|
||||
}
|
||||
|
||||
std::vector<File::IOFile> NFSFileReader::OpenFiles(const std::string& directory,
|
||||
File::IOFile first_file, u64 expected_raw_size,
|
||||
u64* raw_size_out)
|
||||
std::vector<File::DirectIOFile> NFSFileReader::OpenFiles(const std::string& directory,
|
||||
File::DirectIOFile first_file,
|
||||
u64 expected_raw_size, u64* raw_size_out)
|
||||
{
|
||||
const u64 file_count = Common::AlignUp(expected_raw_size, MAX_FILE_SIZE) / MAX_FILE_SIZE;
|
||||
|
||||
std::vector<File::IOFile> files;
|
||||
std::vector<File::DirectIOFile> files;
|
||||
files.reserve(file_count);
|
||||
|
||||
*raw_size_out = first_file.GetSize();
|
||||
@@ -82,8 +82,8 @@ std::vector<File::IOFile> NFSFileReader::OpenFiles(const std::string& directory,
|
||||
for (u64 i = 1; i < file_count; ++i)
|
||||
{
|
||||
const std::string child_path = fmt::format("{}hif_{:06}.nfs", directory, i);
|
||||
File::IOFile child(child_path, "rb");
|
||||
if (!child)
|
||||
File::DirectIOFile child(child_path, File::AccessMode::Read);
|
||||
if (!child.IsOpen())
|
||||
{
|
||||
ERROR_LOG_FMT(DISCIO, "Failed to open {}", child_path);
|
||||
return {};
|
||||
@@ -123,7 +123,7 @@ u64 NFSFileReader::CalculateExpectedDataSize(const std::vector<NFSLBARange>& lba
|
||||
return u64(greatest_block_index) * BLOCK_SIZE;
|
||||
}
|
||||
|
||||
std::unique_ptr<NFSFileReader> NFSFileReader::Create(File::IOFile first_file,
|
||||
std::unique_ptr<NFSFileReader> NFSFileReader::Create(File::DirectIOFile first_file,
|
||||
const std::string& path)
|
||||
{
|
||||
std::string directory, filename, extension;
|
||||
@@ -136,18 +136,15 @@ std::unique_ptr<NFSFileReader> NFSFileReader::Create(File::IOFile first_file,
|
||||
return nullptr;
|
||||
|
||||
NFSHeader header;
|
||||
if (!first_file.Seek(0, File::SeekOrigin::Begin) || !first_file.ReadArray(&header, 1) ||
|
||||
header.magic != NFS_MAGIC)
|
||||
{
|
||||
if (!first_file.OffsetRead(0, Common::AsWritableU8Span(header)) || header.magic != NFS_MAGIC)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<NFSLBARange> lba_ranges = GetLBARanges(header);
|
||||
|
||||
const u64 expected_raw_size = CalculateExpectedRawSize(lba_ranges);
|
||||
|
||||
u64 raw_size;
|
||||
std::vector<File::IOFile> files =
|
||||
std::vector<File::DirectIOFile> files =
|
||||
OpenFiles(directory, std::move(first_file), expected_raw_size, &raw_size);
|
||||
|
||||
if (files.empty())
|
||||
@@ -157,8 +154,8 @@ std::unique_ptr<NFSFileReader> NFSFileReader::Create(File::IOFile first_file,
|
||||
new NFSFileReader(std::move(lba_ranges), std::move(files), key, raw_size));
|
||||
}
|
||||
|
||||
NFSFileReader::NFSFileReader(std::vector<NFSLBARange> lba_ranges, std::vector<File::IOFile> files,
|
||||
Key key, u64 raw_size)
|
||||
NFSFileReader::NFSFileReader(std::vector<NFSLBARange> lba_ranges,
|
||||
std::vector<File::DirectIOFile> files, Key key, u64 raw_size)
|
||||
: m_lba_ranges(std::move(lba_ranges)), m_files(std::move(files)),
|
||||
m_aes_context(Common::AES::CreateContextDecrypt(key.data())), m_raw_size(raw_size), m_key(key)
|
||||
{
|
||||
@@ -167,11 +164,7 @@ NFSFileReader::NFSFileReader(std::vector<NFSLBARange> lba_ranges, std::vector<Fi
|
||||
|
||||
std::unique_ptr<BlobReader> NFSFileReader::CopyReader() const
|
||||
{
|
||||
std::vector<File::IOFile> new_files{};
|
||||
for (const File::IOFile& file : m_files)
|
||||
new_files.push_back(file.Duplicate("rb"));
|
||||
return std::unique_ptr<NFSFileReader>(
|
||||
new NFSFileReader(m_lba_ranges, std::move(new_files), m_key, m_raw_size));
|
||||
return std::unique_ptr<BlobReader>{new NFSFileReader(m_lba_ranges, m_files, m_key, m_raw_size)};
|
||||
}
|
||||
|
||||
u64 NFSFileReader::GetDataSize() const
|
||||
@@ -208,6 +201,7 @@ bool NFSFileReader::ReadEncryptedBlock(u64 physical_block_index)
|
||||
|
||||
const u64 file_index = physical_block_index / BLOCKS_PER_FILE;
|
||||
const u64 block_in_file = physical_block_index % BLOCKS_PER_FILE;
|
||||
const u64 offset_in_file = sizeof(NFSHeader) + block_in_file * BLOCK_SIZE;
|
||||
|
||||
if (block_in_file == BLOCKS_PER_FILE - 1)
|
||||
{
|
||||
@@ -217,33 +211,23 @@ bool NFSFileReader::ReadEncryptedBlock(u64 physical_block_index)
|
||||
constexpr size_t PART_1_SIZE = BLOCK_SIZE - sizeof(NFSHeader);
|
||||
constexpr size_t PART_2_SIZE = sizeof(NFSHeader);
|
||||
|
||||
File::IOFile& file_1 = m_files[file_index];
|
||||
File::IOFile& file_2 = m_files[file_index + 1];
|
||||
File::DirectIOFile& file_1 = m_files[file_index];
|
||||
File::DirectIOFile& file_2 = m_files[file_index + 1];
|
||||
|
||||
if (!file_1.Seek(sizeof(NFSHeader) + block_in_file * BLOCK_SIZE, File::SeekOrigin::Begin) ||
|
||||
!file_1.ReadBytes(m_current_block_encrypted.data(), PART_1_SIZE))
|
||||
{
|
||||
file_1.ClearError();
|
||||
if (!file_1.OffsetRead(offset_in_file, m_current_block_encrypted.data(), PART_1_SIZE))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_2.Seek(0, File::SeekOrigin::Begin) ||
|
||||
!file_2.ReadBytes(m_current_block_encrypted.data() + PART_1_SIZE, PART_2_SIZE))
|
||||
{
|
||||
file_2.ClearError();
|
||||
if (!file_2.OffsetRead(0, m_current_block_encrypted.data() + PART_1_SIZE, PART_2_SIZE))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal case. The read is offset by 0x200 bytes, but it's all within one file.
|
||||
|
||||
File::IOFile& file = m_files[file_index];
|
||||
File::DirectIOFile& file = m_files[file_index];
|
||||
|
||||
if (!file.Seek(sizeof(NFSHeader) + block_in_file * BLOCK_SIZE, File::SeekOrigin::Begin) ||
|
||||
!file.ReadBytes(m_current_block_encrypted.data(), BLOCK_SIZE))
|
||||
if (!file.OffsetRead(offset_in_file, m_current_block_encrypted.data(), BLOCK_SIZE))
|
||||
{
|
||||
file.ClearError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Crypto/AES.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
// This is the file format used for Wii games released on the Wii U eShop.
|
||||
@@ -41,7 +41,7 @@ static_assert(sizeof(NFSHeader) == 0x200);
|
||||
class NFSFileReader final : public BlobReader
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<NFSFileReader> Create(File::IOFile first_file,
|
||||
static std::unique_ptr<NFSFileReader> Create(File::DirectIOFile first_file,
|
||||
const std::string& directory_path);
|
||||
|
||||
BlobType GetBlobType() const override { return BlobType::NFS; }
|
||||
@@ -65,12 +65,13 @@ private:
|
||||
|
||||
static bool ReadKey(const std::string& path, const std::string& directory, Key* key_out);
|
||||
static std::vector<NFSLBARange> GetLBARanges(const NFSHeader& header);
|
||||
static std::vector<File::IOFile> OpenFiles(const std::string& directory, File::IOFile first_file,
|
||||
u64 expected_raw_size, u64* raw_size_out);
|
||||
static std::vector<File::DirectIOFile> OpenFiles(const std::string& directory,
|
||||
File::DirectIOFile first_file,
|
||||
u64 expected_raw_size, u64* raw_size_out);
|
||||
static u64 CalculateExpectedRawSize(const std::vector<NFSLBARange>& lba_ranges);
|
||||
static u64 CalculateExpectedDataSize(const std::vector<NFSLBARange>& lba_ranges);
|
||||
|
||||
NFSFileReader(std::vector<NFSLBARange> lba_ranges, std::vector<File::IOFile> files, Key key,
|
||||
NFSFileReader(std::vector<NFSLBARange> lba_ranges, std::vector<File::DirectIOFile> files, Key key,
|
||||
u64 raw_size);
|
||||
|
||||
u64 ToPhysicalBlockIndex(u64 logical_block_index) const;
|
||||
@@ -83,7 +84,7 @@ private:
|
||||
u64 m_current_logical_block_index = std::numeric_limits<u64>::max();
|
||||
|
||||
std::vector<NFSLBARange> m_lba_ranges;
|
||||
std::vector<File::IOFile> m_files;
|
||||
std::vector<File::DirectIOFile> m_files;
|
||||
std::unique_ptr<Common::AES::Context> m_aes_context;
|
||||
u64 m_raw_size;
|
||||
u64 m_data_size;
|
||||
|
||||
@@ -4,17 +4,11 @@
|
||||
#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)
|
||||
@@ -38,7 +32,7 @@ std::unique_ptr<SplitPlainFileReader> SplitPlainFileReader::Create(std::string_v
|
||||
u64 offset = 0;
|
||||
while (true)
|
||||
{
|
||||
File::IOFile f(fmt::format("{}.part{}.iso", base_path, index), "rb");
|
||||
File::DirectIOFile f(fmt::format("{}.part{}.iso", base_path, index), File::AccessMode::Read);
|
||||
if (!f.IsOpen())
|
||||
break;
|
||||
const u64 size = f.GetSize();
|
||||
@@ -58,13 +52,7 @@ std::unique_ptr<SplitPlainFileReader> SplitPlainFileReader::Create(std::string_v
|
||||
|
||||
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)));
|
||||
return std::unique_ptr<SplitPlainFileReader>{new SplitPlainFileReader(m_files)};
|
||||
}
|
||||
|
||||
bool SplitPlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
@@ -80,13 +68,10 @@ bool SplitPlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
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();
|
||||
const u64 offset_in_file = current_offset - file.offset;
|
||||
const u64 current_read = std::min(file.size - offset_in_file, rest);
|
||||
if (!f.OffsetRead(offset_in_file, out, current_read))
|
||||
return false;
|
||||
}
|
||||
|
||||
rest -= current_read;
|
||||
if (rest == 0)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/DirectIOFile.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
||||
namespace DiscIO
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
private:
|
||||
struct SingleFile
|
||||
{
|
||||
File::IOFile file;
|
||||
File::DirectIOFile file;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/Swap.h"
|
||||
|
||||
namespace
|
||||
@@ -45,11 +44,10 @@ void Replace(u64 offset, u64 size, u8* out_ptr, u64 replace_offset, const T& rep
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
std::unique_ptr<TGCFileReader> TGCFileReader::Create(File::IOFile file)
|
||||
std::unique_ptr<TGCFileReader> TGCFileReader::Create(File::DirectIOFile file)
|
||||
{
|
||||
TGCHeader header;
|
||||
if (file.Seek(0, File::SeekOrigin::Begin) && file.ReadArray(&header, 1) &&
|
||||
header.magic == TGC_MAGIC)
|
||||
if (file.OffsetRead(0, Common::AsWritableU8Span(header)) && header.magic == TGC_MAGIC)
|
||||
{
|
||||
return std::unique_ptr<TGCFileReader>(new TGCFileReader(std::move(file)));
|
||||
}
|
||||
@@ -57,18 +55,16 @@ std::unique_ptr<TGCFileReader> TGCFileReader::Create(File::IOFile file)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TGCFileReader::TGCFileReader(File::IOFile file) : m_file(std::move(file))
|
||||
TGCFileReader::TGCFileReader(File::DirectIOFile file) : m_file(std::move(file))
|
||||
{
|
||||
m_file.Seek(0, File::SeekOrigin::Begin);
|
||||
m_file.ReadArray(&m_header, 1);
|
||||
m_file.OffsetRead(0, Common::AsWritableU8Span(m_header));
|
||||
|
||||
m_size = m_file.GetSize();
|
||||
|
||||
const u32 fst_offset = Common::swap32(m_header.fst_real_offset);
|
||||
const u32 fst_size = Common::swap32(m_header.fst_size);
|
||||
m_fst.resize(fst_size);
|
||||
if (!m_file.Seek(fst_offset, File::SeekOrigin::Begin) ||
|
||||
!m_file.ReadBytes(m_fst.data(), m_fst.size()))
|
||||
if (!m_file.OffsetRead(fst_offset, m_fst))
|
||||
{
|
||||
m_fst.clear();
|
||||
}
|
||||
@@ -100,7 +96,7 @@ TGCFileReader::TGCFileReader(File::IOFile file) : m_file(std::move(file))
|
||||
|
||||
std::unique_ptr<BlobReader> TGCFileReader::CopyReader() const
|
||||
{
|
||||
return Create(m_file.Duplicate("rb"));
|
||||
return Create(m_file);
|
||||
}
|
||||
|
||||
u64 TGCFileReader::GetDataSize() const
|
||||
@@ -112,8 +108,7 @@ bool TGCFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
{
|
||||
const u32 tgc_header_size = Common::swap32(m_header.tgc_header_size);
|
||||
|
||||
if (m_file.Seek(offset + tgc_header_size, File::SeekOrigin::Begin) &&
|
||||
m_file.ReadBytes(out_ptr, nbytes))
|
||||
if (m_file.OffsetRead(offset + tgc_header_size, out_ptr, nbytes))
|
||||
{
|
||||
const u32 replacement_dol_offset = SubtractBE32(m_header.dol_real_offset, tgc_header_size);
|
||||
const u32 replacement_fst_offset = SubtractBE32(m_header.fst_real_offset, tgc_header_size);
|
||||
@@ -126,7 +121,6 @@ bool TGCFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
m_file.ClearError();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user