// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. // Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include "common/archives.h" #include "common/common_types.h" #include "common/file_util.h" namespace Common::Compression { /** * Compresses a source memory region with Zstandard and returns the compressed data in a vector. * * @param source the uncompressed source memory region. * @param compression_level the used compression level. Should be between 1 and 22. * * @return the compressed data. */ [[nodiscard]] std::vector CompressDataZSTD(std::span source, s32 compression_level); /** * Compresses a source memory region with Zstandard with the default compression level and returns * the compressed data in a vector. * * @param source the uncompressed source memory region. * * @return the compressed data. */ [[nodiscard]] std::vector CompressDataZSTDDefault(std::span source); /** * Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector. * * @param compressed the compressed source memory region. * * @return the decompressed data. */ [[nodiscard]] std::vector DecompressDataZSTD(std::span compressed); } // namespace Common::Compression namespace FileUtil { struct Z3DSFileHeader { static constexpr std::array EXPECTED_MAGIC = {'Z', '3', 'D', 'S'}; static constexpr u8 EXPECTED_VERSION = 1; std::array magic = EXPECTED_MAGIC; std::array underlying_magic{}; u8 version = EXPECTED_VERSION; u8 reserved = 0; u16 header_size = 0; u32 metadata_size = 0; u64 compressed_size = 0; u64 uncompressed_size = 0; template void serialize(Archive& ar, const unsigned int) { ar & magic; ar & underlying_magic; ar & version; ar & reserved; ar & header_size; ar & metadata_size; ar & compressed_size; ar & uncompressed_size; } }; static_assert(sizeof(Z3DSFileHeader) == 0x20, "Invalid Z3DSFileHeader size"); class Z3DSMetadata { public: static constexpr u8 METADATA_VERSION = 1; Z3DSMetadata() {} Z3DSMetadata(const std::span& source_data); void Add(const std::string& name, const std::span& data) { items.insert({name, std::vector(data.begin(), data.end())}); } void Add(const std::string& name, const std::string& data) { items.insert({name, std::vector(data.begin(), data.end())}); } std::optional> Get(const std::string& name) const { auto it = items.find(name); if (it == items.end()) { return std::nullopt; } return it->second; } std::vector AsBinary(); private: struct Item { enum Type : u8 { TYPE_END = 0, TYPE_BINARY = 1, }; Type type{}; u8 name_len{}; u16 data_len{}; }; static_assert(sizeof(Item) == 4); std::unordered_map> items; template void serialize(Archive& ar, const unsigned int) { ar & items; } friend class boost::serialization::access; }; class Z3DSWriteIOFile : public IOFile { public: static constexpr size_t DEFAULT_FRAME_SIZE = 256 * 1024; // 256KiB static constexpr size_t DEFAULT_CIA_FRAME_SIZE = 32 * 1024 * 1024; // 32MiB static constexpr size_t MAX_FRAME_SIZE = 0; // Let the lib decide, usually 1GiB Z3DSWriteIOFile(); Z3DSWriteIOFile(std::unique_ptr&& underlying_file, const std::array& underlying_magic, size_t frame_size); ~Z3DSWriteIOFile(); bool Close() override; u64 GetSize() const override; bool Resize(u64 size) override; bool Flush() override; void Clear() override; bool IsCrypto() override; bool IsCompressed() override { return true; } const std::string& Filename() const override; bool IsOpen() const override; bool IsGood() const override; int GetFd() const override; Z3DSMetadata& Metadata() { return metadata; } size_t GetNextWriteHint(); private: struct Z3DSWriteIOFileImpl; bool Open() override; std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override; std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, std::size_t offset) override; std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override; bool SeekImpl(s64 off, int origin) override; u64 TellImpl() const override; std::unique_ptr file; std::unique_ptr impl; u64 written_uncompressed = 0; bool metadata_written = false; Z3DSMetadata metadata; template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; bool is_serializing = false; }; class Z3DSReadIOFile : public IOFile { public: static std::optional GetUnderlyingFileMagic(IOFile* underlying_file); Z3DSReadIOFile(); Z3DSReadIOFile(std::unique_ptr&& underlying_file); ~Z3DSReadIOFile(); bool Close() override; u64 GetSize() const override; bool Resize(u64 size) override; bool Flush() override; void Clear() override; bool IsCrypto() override; bool IsCompressed() override { return true; } const std::string& Filename() const override; bool IsOpen() const override; bool IsGood() const override; int GetFd() const override; std::array GetFileMagic(); const Z3DSMetadata& Metadata(); private: struct Z3DSReadIOFileImpl; static constexpr u32 MakeMagic(char a, char b, char c, char d) { return a | b << 8 | c << 16 | d << 24; } bool Open() override; std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override; std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, std::size_t offset) override; std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override; bool SeekImpl(s64 off, int origin) override; u64 TellImpl() const override; std::unique_ptr file; std::unique_ptr impl; template void serialize(Archive& ar, const unsigned int); friend class boost::serialization::access; bool is_serializing = false; }; using ProgressCallback = void(std::size_t, std::size_t); bool CompressZ3DSFile(const std::string& src_file, const std::string& dst_file, const std::array& underlying_magic, size_t frame_size, std::function&& update_callback = nullptr); bool DeCompressZ3DSFile(const std::string& src_file, const std::string& dst_file, std::function&& update_callback = nullptr); } // namespace FileUtil BOOST_CLASS_EXPORT_KEY(FileUtil::Z3DSWriteIOFile) BOOST_CLASS_EXPORT_KEY(FileUtil::Z3DSReadIOFile)