/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "settings_static_object.hpp" #include "settings_system_data.hpp" namespace ams::settings::impl { namespace { constexpr inline const char MountNameSeparator[] = ":"; constexpr inline const char DirectoryNameSeparator[] = "/"; constexpr inline const char SystemDataFileName[] = "file"; class LazyFileAccessor final { NON_COPYABLE(LazyFileAccessor); NON_MOVEABLE(LazyFileAccessor); private: bool m_is_cached; fs::FileHandle m_file; s64 m_offset; size_t m_size; u8 m_buffer[16_KB]; public: LazyFileAccessor() : m_is_cached(false), m_file(), m_offset(), m_size(), m_buffer() { /* ... */ } Result Open(const char *path, int mode); void Close(); Result Read(s64 offset, void *dst, size_t size); private: bool GetCache(s64 offset, void *dst, size_t size); }; Result LazyFileAccessor::Open(const char *path, int mode) { /* Open our file. */ R_RETURN(fs::OpenFile(std::addressof(m_file), path, mode)); } void LazyFileAccessor::Close() { /* Close our file. */ fs::CloseFile(m_file); /* Reset our state. */ m_is_cached = false; m_file = {}; m_size = 0; m_offset = 0; } bool LazyFileAccessor::GetCache(s64 offset, void *dst, size_t size) { /* Check pre-conditions. */ AMS_ASSERT(offset >= 0); AMS_ASSERT(dst != nullptr); /* Check that we're cached. */ if (!m_is_cached) { return false; } /* Check that offset is within cache. */ if (offset < m_offset) { return false; } /* Check that the read remains within range. */ const size_t offset_in_cache = offset - m_offset; if (m_size < offset_in_cache + size) { return false; } /* Copy the cached data. */ std::memcpy(dst, m_buffer + offset_in_cache, size); return true; } Result LazyFileAccessor::Read(s64 offset, void *dst, size_t size) { /* Check pre-conditions. */ AMS_ASSERT(offset >= 0); AMS_ASSERT(dst != nullptr); /* Try to read from cache. */ R_SUCCEED_IF(this->GetCache(offset, dst, size)); /* If the read is too big for the cache, read the data directly. */ if (size > sizeof(m_buffer)) { R_RETURN(fs::ReadFile(m_file, offset, dst, size)); } /* Get the file size. */ s64 file_size; R_TRY(fs::GetFileSize(std::addressof(file_size), m_file)); /* If the file is too small, read the data directly. */ if (file_size < offset + static_cast(size)) { R_RETURN(fs::ReadFile(m_file, offset, dst, size)); } /* Determine the read size. */ const size_t read_size = std::min(file_size - offset, sizeof(m_buffer)); /* Read into the cache. */ if (read_size > 0) { R_TRY(fs::ReadFile(m_file, offset, m_buffer, read_size)); } /* Update cache statement. */ m_offset = offset; m_size = read_size; m_is_cached = true; /* Get the data from the cache. */ const bool succeeded = this->GetCache(offset, dst, size); AMS_ASSERT(succeeded); AMS_UNUSED(succeeded); R_SUCCEED(); } LazyFileAccessor &GetLazyFileAccessor() { return StaticObject::Get(); } } void SystemData::SetSystemDataId(ncm::SystemDataId id) { m_system_data_id = id; } void SystemData::SetMountName(const char *name) { util::Strlcpy(m_mount_name, name, sizeof(m_mount_name)); int pos = 0; pos += util::Strlcpy(m_file_path + pos, name, sizeof(m_mount_name)); pos += util::Strlcpy(m_file_path + pos, MountNameSeparator, sizeof(MountNameSeparator)); pos += util::Strlcpy(m_file_path + pos, DirectoryNameSeparator, sizeof(DirectoryNameSeparator)); pos += util::Strlcpy(m_file_path + pos, SystemDataFileName, sizeof(SystemDataFileName)); } Result SystemData::Mount() { R_RETURN(fs::MountSystemData(m_mount_name, m_system_data_id)); } Result SystemData::OpenToRead() { R_RETURN(GetLazyFileAccessor().Open(m_file_path, fs::OpenMode_Read)); } void SystemData::Close() { return GetLazyFileAccessor().Close(); } Result SystemData::Read(s64 offset, void *dst, size_t size) { R_RETURN(GetLazyFileAccessor().Read(offset, dst, size)); } }