WIABlob.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. // Copyright 2018 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <array>
  5. #include <limits>
  6. #include <map>
  7. #include <memory>
  8. #include <mutex>
  9. #include <type_traits>
  10. #include <utility>
  11. #include "Common/CommonTypes.h"
  12. #include "Common/Crypto/SHA1.h"
  13. #include "Common/IOFile.h"
  14. #include "Common/Swap.h"
  15. #include "DiscIO/Blob.h"
  16. #include "DiscIO/MultithreadedCompressor.h"
  17. #include "DiscIO/WIACompression.h"
  18. #include "DiscIO/WiiEncryptionCache.h"
  19. namespace DiscIO
  20. {
  21. class FileSystem;
  22. class VolumeDisc;
  23. enum class WIARVZCompressionType : u32
  24. {
  25. None = 0,
  26. Purge = 1,
  27. Bzip2 = 2,
  28. LZMA = 3,
  29. LZMA2 = 4,
  30. Zstd = 5,
  31. };
  32. std::pair<int, int> GetAllowedCompressionLevels(WIARVZCompressionType compression_type, bool gui);
  33. constexpr u32 WIA_MAGIC = 0x01414957; // "WIA\x1" (byteswapped to little endian)
  34. constexpr u32 RVZ_MAGIC = 0x015A5652; // "RVZ\x1" (byteswapped to little endian)
  35. template <bool RVZ>
  36. class WIARVZFileReader : public BlobReader
  37. {
  38. public:
  39. ~WIARVZFileReader();
  40. static std::unique_ptr<WIARVZFileReader> Create(File::IOFile file, const std::string& path);
  41. BlobType GetBlobType() const override;
  42. std::unique_ptr<BlobReader> CopyReader() const override;
  43. u64 GetRawSize() const override { return Common::swap64(m_header_1.wia_file_size); }
  44. u64 GetDataSize() const override { return Common::swap64(m_header_1.iso_file_size); }
  45. DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; }
  46. u64 GetBlockSize() const override { return Common::swap32(m_header_2.chunk_size); }
  47. bool HasFastRandomAccessInBlock() const override { return false; }
  48. std::string GetCompressionMethod() const override;
  49. std::optional<int> GetCompressionLevel() const override
  50. {
  51. return static_cast<int>(static_cast<s32>(Common::swap32(m_header_2.compression_level)));
  52. }
  53. bool Read(u64 offset, u64 size, u8* out_ptr) override;
  54. bool SupportsReadWiiDecrypted(u64 offset, u64 size, u64 partition_data_offset) const override;
  55. bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_data_offset) override;
  56. static ConversionResultCode Convert(BlobReader* infile, const VolumeDisc* infile_volume,
  57. File::IOFile* outfile, WIARVZCompressionType compression_type,
  58. int compression_level, int chunk_size, CompressCB callback);
  59. private:
  60. using WiiKey = std::array<u8, 16>;
  61. // See docs/WiaAndRvz.md for details about the format
  62. #pragma pack(push, 1)
  63. struct WIAHeader1
  64. {
  65. u32 magic;
  66. u32 version;
  67. u32 version_compatible;
  68. u32 header_2_size;
  69. Common::SHA1::Digest header_2_hash;
  70. u64 iso_file_size;
  71. u64 wia_file_size;
  72. Common::SHA1::Digest header_1_hash;
  73. };
  74. static_assert(sizeof(WIAHeader1) == 0x48, "Wrong size for WIA header 1");
  75. struct WIAHeader2
  76. {
  77. u32 disc_type;
  78. u32 compression_type;
  79. s32 compression_level; // Informative only
  80. u32 chunk_size;
  81. std::array<u8, 0x80> disc_header;
  82. u32 number_of_partition_entries;
  83. u32 partition_entry_size;
  84. u64 partition_entries_offset;
  85. Common::SHA1::Digest partition_entries_hash;
  86. u32 number_of_raw_data_entries;
  87. u64 raw_data_entries_offset;
  88. u32 raw_data_entries_size;
  89. u32 number_of_group_entries;
  90. u64 group_entries_offset;
  91. u32 group_entries_size;
  92. u8 compressor_data_size;
  93. u8 compressor_data[7];
  94. };
  95. static_assert(sizeof(WIAHeader2) == 0xdc, "Wrong size for WIA header 2");
  96. struct PartitionDataEntry
  97. {
  98. u32 first_sector;
  99. u32 number_of_sectors;
  100. u32 group_index;
  101. u32 number_of_groups;
  102. };
  103. static_assert(sizeof(PartitionDataEntry) == 0x10, "Wrong size for WIA partition data entry");
  104. struct PartitionEntry
  105. {
  106. WiiKey partition_key;
  107. std::array<PartitionDataEntry, 2> data_entries;
  108. };
  109. static_assert(sizeof(PartitionEntry) == 0x30, "Wrong size for WIA partition entry");
  110. struct RawDataEntry
  111. {
  112. u64 data_offset;
  113. u64 data_size;
  114. u32 group_index;
  115. u32 number_of_groups;
  116. };
  117. static_assert(sizeof(RawDataEntry) == 0x18, "Wrong size for WIA raw data entry");
  118. struct WIAGroupEntry
  119. {
  120. u32 data_offset; // >> 2
  121. u32 data_size;
  122. };
  123. static_assert(sizeof(WIAGroupEntry) == 0x08, "Wrong size for WIA group entry");
  124. struct RVZGroupEntry
  125. {
  126. u32 data_offset; // >> 2
  127. u32 data_size;
  128. u32 rvz_packed_size;
  129. };
  130. static_assert(sizeof(RVZGroupEntry) == 0x0c, "Wrong size for RVZ group entry");
  131. using GroupEntry = std::conditional_t<RVZ, RVZGroupEntry, WIAGroupEntry>;
  132. struct HashExceptionEntry
  133. {
  134. u16 offset;
  135. Common::SHA1::Digest hash;
  136. };
  137. static_assert(sizeof(HashExceptionEntry) == 0x16, "Wrong size for WIA hash exception entry");
  138. #pragma pack(pop)
  139. struct DataEntry
  140. {
  141. u32 index;
  142. bool is_partition;
  143. u8 partition_data_index;
  144. DataEntry(size_t index_)
  145. : index(static_cast<u32>(index_)), is_partition(false), partition_data_index(0)
  146. {
  147. }
  148. DataEntry(size_t index_, size_t partition_data_index_)
  149. : index(static_cast<u32>(index_)), is_partition(true),
  150. partition_data_index(static_cast<u8>(partition_data_index_))
  151. {
  152. }
  153. };
  154. class Chunk
  155. {
  156. public:
  157. Chunk();
  158. Chunk(File::IOFile* file, u64 offset_in_file, u64 compressed_size, u64 decompressed_size,
  159. u32 exception_lists, bool compressed_exception_lists, u32 rvz_packed_size,
  160. u64 data_offset, std::unique_ptr<Decompressor> decompressor);
  161. bool Read(u64 offset, u64 size, u8* out_ptr);
  162. // This can only be called once at least one byte of data has been read
  163. void GetHashExceptions(std::vector<HashExceptionEntry>* exception_list,
  164. u64 exception_list_index, u16 additional_offset) const;
  165. template <typename T>
  166. bool ReadAll(std::vector<T>* vector)
  167. {
  168. return Read(0, vector->size() * sizeof(T), reinterpret_cast<u8*>(vector->data()));
  169. }
  170. private:
  171. bool Decompress();
  172. bool HandleExceptions(const u8* data, size_t bytes_allocated, size_t bytes_written,
  173. size_t* bytes_used, bool align);
  174. size_t GetOutBytesWrittenExcludingExceptions() const;
  175. DecompressionBuffer m_in;
  176. DecompressionBuffer m_out;
  177. size_t m_in_bytes_read = 0;
  178. std::unique_ptr<Decompressor> m_decompressor = nullptr;
  179. File::IOFile* m_file = nullptr;
  180. u64 m_offset_in_file = 0;
  181. size_t m_out_bytes_allocated_for_exceptions = 0;
  182. size_t m_out_bytes_used_for_exceptions = 0;
  183. size_t m_in_bytes_used_for_exceptions = 0;
  184. u32 m_exception_lists = 0;
  185. bool m_compressed_exception_lists = false;
  186. u32 m_rvz_packed_size = 0;
  187. u64 m_data_offset = 0;
  188. };
  189. explicit WIARVZFileReader(File::IOFile file, const std::string& path);
  190. bool Initialize(const std::string& path);
  191. bool HasDataOverlap() const;
  192. const PartitionEntry* GetPartition(u64 partition_data_offset, u32* partition_first_sector) const;
  193. bool ReadFromGroups(u64* offset, u64* size, u8** out_ptr, u64 chunk_size, u32 sector_size,
  194. u64 data_offset, u64 data_size, u32 group_index, u32 number_of_groups,
  195. u32 exception_lists);
  196. Chunk& ReadCompressedData(u64 offset_in_file, u64 compressed_size, u64 decompressed_size,
  197. WIARVZCompressionType compression_type, u32 exception_lists = 0,
  198. u32 rvz_packed_size = 0, u64 data_offset = 0);
  199. static bool ApplyHashExceptions(const std::vector<HashExceptionEntry>& exception_list,
  200. VolumeWii::HashBlock hash_blocks[VolumeWii::BLOCKS_PER_GROUP]);
  201. static std::string VersionToString(u32 version);
  202. struct ReuseID
  203. {
  204. bool operator==(const ReuseID& other) const
  205. {
  206. return std::tie(partition_key, data_size, encrypted, value) ==
  207. std::tie(other.partition_key, other.data_size, other.encrypted, other.value);
  208. }
  209. bool operator<(const ReuseID& other) const
  210. {
  211. return std::tie(partition_key, data_size, encrypted, value) <
  212. std::tie(other.partition_key, other.data_size, other.encrypted, other.value);
  213. }
  214. bool operator>(const ReuseID& other) const
  215. {
  216. return std::tie(partition_key, data_size, encrypted, value) >
  217. std::tie(other.partition_key, other.data_size, other.encrypted, other.value);
  218. }
  219. bool operator>=(const ReuseID& other) const { return !operator<(other); }
  220. bool operator<=(const ReuseID& other) const { return !operator>(other); }
  221. WiiKey partition_key;
  222. u64 data_size;
  223. bool encrypted;
  224. u8 value;
  225. };
  226. struct CompressThreadState
  227. {
  228. using WiiBlockData = std::array<u8, VolumeWii::BLOCK_DATA_SIZE>;
  229. std::unique_ptr<Compressor> compressor;
  230. std::vector<WiiBlockData> decryption_buffer =
  231. std::vector<WiiBlockData>(VolumeWii::BLOCKS_PER_GROUP);
  232. std::vector<VolumeWii::HashBlock> hash_buffer =
  233. std::vector<VolumeWii::HashBlock>(VolumeWii::BLOCKS_PER_GROUP);
  234. };
  235. struct CompressParameters
  236. {
  237. std::vector<u8> data{};
  238. const DataEntry* data_entry = nullptr;
  239. u64 data_offset = 0;
  240. u64 bytes_read = 0;
  241. size_t group_index = 0;
  242. };
  243. struct WIAOutputParametersEntry
  244. {
  245. std::vector<u8> exception_lists;
  246. std::vector<u8> main_data;
  247. std::optional<ReuseID> reuse_id;
  248. std::optional<GroupEntry> reused_group;
  249. };
  250. struct RVZOutputParametersEntry
  251. {
  252. std::vector<u8> exception_lists;
  253. std::vector<u8> main_data;
  254. std::optional<ReuseID> reuse_id;
  255. std::optional<GroupEntry> reused_group;
  256. size_t rvz_packed_size = 0;
  257. bool compressed = false;
  258. };
  259. using OutputParametersEntry =
  260. std::conditional_t<RVZ, RVZOutputParametersEntry, WIAOutputParametersEntry>;
  261. struct OutputParameters
  262. {
  263. std::vector<OutputParametersEntry> entries;
  264. u64 bytes_read = 0;
  265. size_t group_index = 0;
  266. };
  267. static bool PadTo4(File::IOFile* file, u64* bytes_written);
  268. static void AddRawDataEntry(u64 offset, u64 size, int chunk_size, u32* total_groups,
  269. std::vector<RawDataEntry>* raw_data_entries,
  270. std::vector<DataEntry>* data_entries);
  271. static PartitionDataEntry
  272. CreatePartitionDataEntry(u64 offset, u64 size, u32 index, int chunk_size, u32* total_groups,
  273. const std::vector<PartitionEntry>& partition_entries,
  274. std::vector<DataEntry>* data_entries);
  275. static ConversionResultCode SetUpDataEntriesForWriting(
  276. const VolumeDisc* volume, int chunk_size, u64 iso_size, u32* total_groups,
  277. std::vector<PartitionEntry>* partition_entries, std::vector<RawDataEntry>* raw_data_entries,
  278. std::vector<DataEntry>* data_entries, std::vector<const FileSystem*>* partition_file_systems);
  279. static std::optional<std::vector<u8>> Compress(Compressor* compressor, const u8* data,
  280. size_t size);
  281. static bool WriteHeader(File::IOFile* file, const u8* data, size_t size, u64 upper_bound,
  282. u64* bytes_written, u64* offset_out);
  283. static void SetUpCompressor(std::unique_ptr<Compressor>* compressor,
  284. WIARVZCompressionType compression_type, int compression_level,
  285. WIAHeader2* header_2);
  286. static bool TryReuse(std::map<ReuseID, GroupEntry>* reusable_groups,
  287. std::mutex* reusable_groups_mutex, OutputParametersEntry* entry);
  288. static ConversionResult<OutputParameters>
  289. ProcessAndCompress(CompressThreadState* state, CompressParameters parameters,
  290. const std::vector<PartitionEntry>& partition_entries,
  291. const std::vector<DataEntry>& data_entries, const FileSystem* file_system,
  292. std::map<ReuseID, GroupEntry>* reusable_groups,
  293. std::mutex* reusable_groups_mutex, u64 chunks_per_wii_group,
  294. u64 exception_lists_per_chunk, bool compressed_exception_lists,
  295. bool compression);
  296. static ConversionResultCode Output(std::vector<OutputParametersEntry>* entries,
  297. File::IOFile* outfile,
  298. std::map<ReuseID, GroupEntry>* reusable_groups,
  299. std::mutex* reusable_groups_mutex, GroupEntry* group_entry,
  300. u64* bytes_written);
  301. static ConversionResultCode RunCallback(size_t groups_written, u64 bytes_read, u64 bytes_written,
  302. u32 total_groups, u64 iso_size, CompressCB callback);
  303. bool m_valid;
  304. WIARVZCompressionType m_compression_type;
  305. File::IOFile m_file;
  306. std::string m_path;
  307. Chunk m_cached_chunk;
  308. u64 m_cached_chunk_offset = std::numeric_limits<u64>::max();
  309. WiiEncryptionCache m_encryption_cache;
  310. std::vector<HashExceptionEntry> m_exception_list;
  311. bool m_write_to_exception_list = false;
  312. u64 m_exception_list_last_group_index;
  313. WIAHeader1 m_header_1;
  314. WIAHeader2 m_header_2;
  315. std::vector<PartitionEntry> m_partition_entries;
  316. std::vector<RawDataEntry> m_raw_data_entries;
  317. std::vector<GroupEntry> m_group_entries;
  318. std::map<u64, DataEntry> m_data_entries;
  319. // Perhaps we could set WIA_VERSION_WRITE_COMPATIBLE to 0.9, but WIA version 0.9 was never in
  320. // any official release of wit, and interim versions (either source or binaries) are hard to find.
  321. // Since we've been unable to check if we're write compatible with 0.9, we set it 1.0 to be safe.
  322. static constexpr u32 WIA_VERSION = 0x01000000;
  323. static constexpr u32 WIA_VERSION_WRITE_COMPATIBLE = 0x01000000;
  324. static constexpr u32 WIA_VERSION_READ_COMPATIBLE = 0x00080000;
  325. static constexpr u32 RVZ_VERSION = 0x01000000;
  326. static constexpr u32 RVZ_VERSION_WRITE_COMPATIBLE = 0x00030000;
  327. static constexpr u32 RVZ_VERSION_READ_COMPATIBLE = 0x00030000;
  328. };
  329. using WIAFileReader = WIARVZFileReader<false>;
  330. using RVZFileReader = WIARVZFileReader<true>;
  331. } // namespace DiscIO