From 86f6b6b7b2d930e8203114332b04a5c49a780b06 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 10 Aug 2023 21:34:43 -0400 Subject: vfs: expand support for NCA reading --- .../fssystem_alignment_matching_storage_impl.cpp | 204 +++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.cpp (limited to 'src/core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.cpp') diff --git a/src/core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.cpp b/src/core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.cpp new file mode 100644 index 000000000..641c888ae --- /dev/null +++ b/src/core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.cpp @@ -0,0 +1,204 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "core/file_sys/fssystem/fssystem_alignment_matching_storage_impl.h" + +namespace FileSys { + +namespace { + +template +constexpr size_t GetRoundDownDifference(T x, size_t align) { + return static_cast(x - Common::AlignDown(x, align)); +} + +template +constexpr size_t GetRoundUpDifference(T x, size_t align) { + return static_cast(Common::AlignUp(x, align) - x); +} + +template +size_t GetRoundUpDifference(T* x, size_t align) { + return GetRoundUpDifference(reinterpret_cast(x), align); +} + +} // namespace + +size_t AlignmentMatchingStorageImpl::Read(VirtualFile base_storage, char* work_buf, + size_t work_buf_size, size_t data_alignment, + size_t buffer_alignment, s64 offset, u8* buffer, + size_t size) { + // Check preconditions. + ASSERT(work_buf_size >= data_alignment); + + // Succeed if zero size. + if (size == 0) { + return size; + } + + // Validate arguments. + ASSERT(buffer != nullptr); + + // Determine extents. + u8* aligned_core_buffer; + s64 core_offset; + size_t core_size; + size_t buffer_gap; + size_t offset_gap; + s64 covered_offset; + + const size_t offset_round_up_difference = GetRoundUpDifference(offset, data_alignment); + if (Common::IsAligned(reinterpret_cast(buffer) + offset_round_up_difference, + buffer_alignment)) { + aligned_core_buffer = buffer + offset_round_up_difference; + + core_offset = Common::AlignUp(offset, data_alignment); + core_size = (size < offset_round_up_difference) + ? 0 + : Common::AlignDown(size - offset_round_up_difference, data_alignment); + buffer_gap = 0; + offset_gap = 0; + + covered_offset = core_size > 0 ? core_offset : offset; + } else { + const size_t buffer_round_up_difference = GetRoundUpDifference(buffer, buffer_alignment); + + aligned_core_buffer = buffer + buffer_round_up_difference; + + core_offset = Common::AlignDown(offset, data_alignment); + core_size = (size < buffer_round_up_difference) + ? 0 + : Common::AlignDown(size - buffer_round_up_difference, data_alignment); + buffer_gap = buffer_round_up_difference; + offset_gap = GetRoundDownDifference(offset, data_alignment); + + covered_offset = offset; + } + + // Read the core portion. + if (core_size > 0) { + base_storage->Read(aligned_core_buffer, core_size, core_offset); + + if (offset_gap != 0 || buffer_gap != 0) { + std::memmove(aligned_core_buffer - buffer_gap, aligned_core_buffer + offset_gap, + core_size - offset_gap); + core_size -= offset_gap; + } + } + + // Handle the head portion. + if (offset < covered_offset) { + const s64 head_offset = Common::AlignDown(offset, data_alignment); + const size_t head_size = static_cast(covered_offset - offset); + + ASSERT(GetRoundDownDifference(offset, data_alignment) + head_size <= work_buf_size); + + base_storage->Read(reinterpret_cast(work_buf), data_alignment, head_offset); + std::memcpy(buffer, work_buf + GetRoundDownDifference(offset, data_alignment), head_size); + } + + // Handle the tail portion. + s64 tail_offset = covered_offset + core_size; + size_t remaining_tail_size = static_cast((offset + size) - tail_offset); + while (remaining_tail_size > 0) { + const auto aligned_tail_offset = Common::AlignDown(tail_offset, data_alignment); + const auto cur_size = + std::min(static_cast(aligned_tail_offset + data_alignment - tail_offset), + remaining_tail_size); + base_storage->Read(reinterpret_cast(work_buf), data_alignment, aligned_tail_offset); + + ASSERT((tail_offset - offset) + cur_size <= size); + ASSERT((tail_offset - aligned_tail_offset) + cur_size <= data_alignment); + std::memcpy(reinterpret_cast(buffer) + (tail_offset - offset), + work_buf + (tail_offset - aligned_tail_offset), cur_size); + + remaining_tail_size -= cur_size; + tail_offset += cur_size; + } + + return size; +} + +size_t AlignmentMatchingStorageImpl::Write(VirtualFile base_storage, char* work_buf, + size_t work_buf_size, size_t data_alignment, + size_t buffer_alignment, s64 offset, const u8* buffer, + size_t size) { + // Check preconditions. + ASSERT(work_buf_size >= data_alignment); + + // Succeed if zero size. + if (size == 0) { + return size; + } + + // Validate arguments. + ASSERT(buffer != nullptr); + + // Determine extents. + const u8* aligned_core_buffer; + s64 core_offset; + size_t core_size; + s64 covered_offset; + + const size_t offset_round_up_difference = GetRoundUpDifference(offset, data_alignment); + if (Common::IsAligned(reinterpret_cast(buffer) + offset_round_up_difference, + buffer_alignment)) { + aligned_core_buffer = buffer + offset_round_up_difference; + + core_offset = Common::AlignUp(offset, data_alignment); + core_size = (size < offset_round_up_difference) + ? 0 + : Common::AlignDown(size - offset_round_up_difference, data_alignment); + + covered_offset = core_size > 0 ? core_offset : offset; + } else { + aligned_core_buffer = nullptr; + + core_offset = Common::AlignDown(offset, data_alignment); + core_size = 0; + + covered_offset = offset; + } + + // Write the core portion. + if (core_size > 0) { + base_storage->Write(aligned_core_buffer, core_size, core_offset); + } + + // Handle the head portion. + if (offset < covered_offset) { + const s64 head_offset = Common::AlignDown(offset, data_alignment); + const size_t head_size = static_cast(covered_offset - offset); + + ASSERT((offset - head_offset) + head_size <= data_alignment); + + base_storage->Read(reinterpret_cast(work_buf), data_alignment, head_offset); + std::memcpy(work_buf + (offset - head_offset), buffer, head_size); + base_storage->Write(reinterpret_cast(work_buf), data_alignment, head_offset); + } + + // Handle the tail portion. + s64 tail_offset = covered_offset + core_size; + size_t remaining_tail_size = static_cast((offset + size) - tail_offset); + while (remaining_tail_size > 0) { + ASSERT(static_cast(tail_offset - offset) < size); + + const auto aligned_tail_offset = Common::AlignDown(tail_offset, data_alignment); + const auto cur_size = + std::min(static_cast(aligned_tail_offset + data_alignment - tail_offset), + remaining_tail_size); + + base_storage->Read(reinterpret_cast(work_buf), data_alignment, aligned_tail_offset); + std::memcpy(work_buf + GetRoundDownDifference(tail_offset, data_alignment), + buffer + (tail_offset - offset), cur_size); + base_storage->Write(reinterpret_cast(work_buf), data_alignment, aligned_tail_offset); + + remaining_tail_size -= cur_size; + tail_offset += cur_size; + } + + return size; +} + +} // namespace FileSys -- cgit v1.2.3