/* * 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 . */ #pragma once #include #include "fusee_fs_api.hpp" #include "../fusee_mmc.hpp" #include "../fusee_fatal.hpp" #include "../fusee_sd_card.hpp" namespace ams::fs { class IStorage { public: virtual Result Read(s64 offset, void *buffer, size_t size) = 0; virtual Result Write(s64 offset, const void *buffer, size_t size) = 0; virtual Result Flush() = 0; virtual Result SetSize(s64 size) = 0; virtual Result GetSize(s64 *out) = 0; public: static inline Result CheckAccessRange(s64 offset, s64 size, s64 total_size) { R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); R_UNLESS(size >= 0, fs::ResultInvalidSize()); R_UNLESS(util::CanAddWithoutOverflow(offset, size), fs::ResultOutOfRange()); R_UNLESS(offset + size <= total_size, fs::ResultOutOfRange()); R_SUCCEED(); } static ALWAYS_INLINE Result CheckAccessRange(s64 offset, size_t size, s64 total_size) { R_RETURN(CheckAccessRange(offset, static_cast(size), total_size)); } static inline Result CheckOffsetAndSize(s64 offset, s64 size) { R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); R_UNLESS(size >= 0, fs::ResultInvalidSize()); R_UNLESS(util::CanAddWithoutOverflow(offset, size), fs::ResultOutOfRange()); R_SUCCEED(); } static ALWAYS_INLINE Result CheckOffsetAndSize(s64 offset, size_t size) { R_RETURN(CheckOffsetAndSize(offset, static_cast(size))); } static inline Result CheckOffsetAndSizeWithResult(s64 offset, s64 size, Result fail_result) { R_TRY_CATCH(CheckOffsetAndSize(offset, size)) { R_CONVERT_ALL(fail_result); } R_END_TRY_CATCH; R_SUCCEED(); } static ALWAYS_INLINE Result CheckOffsetAndSizeWithResult(s64 offset, size_t size, Result fail_result) { R_RETURN(CheckOffsetAndSizeWithResult(offset, static_cast(size), fail_result)); } }; class ReadOnlyStorageAdapter : public IStorage { private: IStorage &m_storage; public: ReadOnlyStorageAdapter(IStorage &s) : m_storage(s) { /* ... */ } virtual Result Read(s64 offset, void *buffer, size_t size) override { R_RETURN(m_storage.Read(offset, buffer, size)); } virtual Result Flush() override { R_RETURN(m_storage.Flush()); } virtual Result GetSize(s64 *out) override { R_RETURN(m_storage.GetSize(out)); } virtual Result Write(s64 offset, const void *buffer, size_t size) override { R_THROW(fs::ResultUnsupportedOperation()); } virtual Result SetSize(s64 size) override { R_THROW(fs::ResultUnsupportedOperation()); } }; class SubStorage : public IStorage { private: IStorage &m_storage; s64 m_offset; s64 m_size; public: SubStorage(IStorage &s, s64 o, s64 sz) : m_storage(s), m_offset(o), m_size(sz) { /* ... */ } virtual Result Read(s64 offset, void *buffer, size_t size) override { /* Succeed immediately on zero-sized operation. */ R_SUCCEED_IF(size == 0); /* Validate arguments and read. */ R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_TRY(IStorage::CheckAccessRange(offset, size, m_size)); R_RETURN(m_storage.Read(m_offset + offset, buffer, size)); } virtual Result Write(s64 offset, const void *buffer, size_t size) override{ /* Succeed immediately on zero-sized operation. */ R_SUCCEED_IF(size == 0); /* Validate arguments and write. */ R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_TRY(IStorage::CheckAccessRange(offset, size, m_size)); R_RETURN(m_storage.Write(m_offset + offset, buffer, size)); } virtual Result Flush() override { R_RETURN(m_storage.Flush()); } virtual Result GetSize(s64 *out) override { *out = m_size; R_SUCCEED(); } virtual Result SetSize(s64 size) override { R_THROW(fs::ResultUnsupportedSetSizeForNotResizableSubStorage()); } }; class FileHandleStorage : public IStorage { private: static constexpr s64 InvalidSize = -1; private: FileHandle m_handle; s64 m_size; public: constexpr explicit FileHandleStorage(FileHandle handle) : m_handle(handle), m_size(InvalidSize) { /* ... */ } ~FileHandleStorage() { fs::CloseFile(m_handle); } protected: Result UpdateSize(); public: virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; virtual Result Flush() override; virtual Result GetSize(s64 *out_size) override; virtual Result SetSize(s64 size) override; }; class SdCardStorage : public fs::IStorage { public: virtual Result Read(s64 offset, void *buffer, size_t size) override { if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) { nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast(offset), static_cast(size)); } R_RETURN(nxboot::ReadSdCard(buffer, size, offset / sdmmc::SectorSize, size / sdmmc::SectorSize)); } virtual Result Flush() override { R_SUCCEED(); } virtual Result GetSize(s64 *out) override { u32 num_sectors; R_TRY(nxboot::GetSdCardMemoryCapacity(std::addressof(num_sectors))); *out = static_cast(num_sectors) * static_cast(sdmmc::SectorSize); R_SUCCEED(); } virtual Result Write(s64 offset, const void *buffer, size_t size) override { if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) { nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast(offset), static_cast(size)); } R_RETURN(nxboot::WriteSdCard(offset / sdmmc::SectorSize, size / sdmmc::SectorSize, buffer, size)); } virtual Result SetSize(s64 size) override { R_THROW(fs::ResultUnsupportedOperation()); } }; template class MmcPartitionStorage : public fs::IStorage { public: constexpr MmcPartitionStorage() { /* ... */ } virtual Result Read(s64 offset, void *buffer, size_t size) override { if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) { nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast(offset), static_cast(size)); } R_RETURN(nxboot::ReadMmc(buffer, size, Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize)); } virtual Result Flush() override { R_SUCCEED(); } virtual Result GetSize(s64 *out) override { u32 num_sectors; R_TRY(nxboot::GetMmcMemoryCapacity(std::addressof(num_sectors), Partition)); *out = static_cast(num_sectors) * static_cast(sdmmc::SectorSize); R_SUCCEED(); } virtual Result Write(s64 offset, const void *buffer, size_t size) override { if (!util::IsAligned(offset, sdmmc::SectorSize) || !util::IsAligned(size, sdmmc::SectorSize)) { nxboot::ShowFatalError("SdCard: unaligned access to %" PRIx64 ", size=%" PRIx64"\n", static_cast(offset), static_cast(size)); } R_RETURN(nxboot::WriteMmc(Partition, offset / sdmmc::SectorSize, size / sdmmc::SectorSize, buffer, size)); } virtual Result SetSize(s64 size) override { R_THROW(fs::ResultUnsupportedOperation()); } }; class MultiFileStorage : public fs::IStorage { private: s64 m_file_size; fs::FileHandle m_handles[64]; bool m_open[64]; int m_file_path_ofs; char m_base_path[0x300]; private: void EnsureFile(int id); public: MultiFileStorage(const char *base_path, OpenMode mode); virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Flush() override; virtual Result GetSize(s64 *out) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; virtual Result SetSize(s64 size) override; }; }