1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <array>
8 #include <cstddef>
9 #include <map>
10 #include <memory>
11 #include <optional>
12 #include <set>
13 #include <string>
14 #include <variant>
15 #include <vector>
16 
17 #include "Common/CommonTypes.h"
18 #include "Common/FileUtil.h"
19 #include "DiscIO/Blob.h"
20 #include "DiscIO/WiiEncryptionCache.h"
21 
22 namespace File
23 {
24 struct FSTEntry;
25 class IOFile;
26 }  // namespace File
27 
28 namespace DiscIO
29 {
30 enum class PartitionType : u32;
31 
32 class DirectoryBlobReader;
33 
34 // Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
35 bool ShouldHideFromGameList(const std::string& volume_path);
36 
37 class DiscContent
38 {
39 public:
40   using ContentSource =
41       std::variant<std::string,          // File
42                    const u8*,            // Memory
43                    DirectoryBlobReader*  // Partition (which one it is is determined by m_offset)
44                    >;
45 
46   DiscContent(u64 offset, u64 size, const std::string& path);
47   DiscContent(u64 offset, u64 size, const u8* data);
48   DiscContent(u64 offset, u64 size, DirectoryBlobReader* blob);
49 
50   // Provided because it's convenient when searching for DiscContent in an std::set
51   explicit DiscContent(u64 offset);
52 
53   u64 GetOffset() const;
54   u64 GetEndOffset() const;
55   u64 GetSize() const;
56   bool Read(u64* offset, u64* length, u8** buffer) const;
57 
58   bool operator==(const DiscContent& other) const { return GetEndOffset() == other.GetEndOffset(); }
59   bool operator!=(const DiscContent& other) const { return !(*this == other); }
60   bool operator<(const DiscContent& other) const { return GetEndOffset() < other.GetEndOffset(); }
61   bool operator>(const DiscContent& other) const { return other < *this; }
62   bool operator<=(const DiscContent& other) const { return !(*this < other); }
63   bool operator>=(const DiscContent& other) const { return !(*this > other); }
64 
65 private:
66   u64 m_offset;
67   u64 m_size = 0;
68   ContentSource m_content_source;
69 };
70 
71 class DiscContentContainer
72 {
73 public:
74   template <typename T>
Add(u64 offset,const std::vector<T> & vector)75   void Add(u64 offset, const std::vector<T>& vector)
76   {
77     return Add(offset, vector.size() * sizeof(T), reinterpret_cast<const u8*>(vector.data()));
78   }
79   void Add(u64 offset, u64 size, const std::string& path);
80   void Add(u64 offset, u64 size, const u8* data);
81   void Add(u64 offset, u64 size, DirectoryBlobReader* blob);
82   u64 CheckSizeAndAdd(u64 offset, const std::string& path);
83   u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);
84 
85   bool Read(u64 offset, u64 length, u8* buffer) const;
86 
87 private:
88   std::set<DiscContent> m_contents;
89 };
90 
91 class DirectoryBlobPartition
92 {
93 public:
94   DirectoryBlobPartition() = default;
95   DirectoryBlobPartition(const std::string& root_directory, std::optional<bool> is_wii);
96 
97   // We do not allow copying, because it might mess up the pointers inside DiscContents
98   DirectoryBlobPartition(const DirectoryBlobPartition&) = delete;
99   DirectoryBlobPartition& operator=(const DirectoryBlobPartition&) = delete;
100   DirectoryBlobPartition(DirectoryBlobPartition&&) = default;
101   DirectoryBlobPartition& operator=(DirectoryBlobPartition&&) = default;
102 
IsWii()103   bool IsWii() const { return m_is_wii; }
GetDataSize()104   u64 GetDataSize() const { return m_data_size; }
GetRootDirectory()105   const std::string& GetRootDirectory() const { return m_root_directory; }
GetHeader()106   const std::vector<u8>& GetHeader() const { return m_disc_header; }
GetContents()107   const DiscContentContainer& GetContents() const { return m_contents; }
108 
GetKey()109   const std::array<u8, VolumeWii::AES_KEY_SIZE>& GetKey() const { return m_key; }
SetKey(std::array<u8,VolumeWii::AES_KEY_SIZE> key)110   void SetKey(std::array<u8, VolumeWii::AES_KEY_SIZE> key) { m_key = key; }
111 
112 private:
113   void SetDiscHeaderAndDiscType(std::optional<bool> is_wii);
114   void SetBI2();
115 
116   // Returns DOL address
117   u64 SetApploader();
118   // Returns FST address
119   u64 SetDOL(u64 dol_address);
120 
121   void BuildFST(u64 fst_address);
122 
123   // FST creation
124   void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
125                       u32 address_shift);
126   void WriteEntryName(u32* name_offset, const std::string& name, u64 name_table_offset);
127   void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
128                       u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
129 
130   DiscContentContainer m_contents;
131   std::vector<u8> m_disc_header;
132   std::vector<u8> m_bi2;
133   std::vector<u8> m_apploader;
134   std::vector<u8> m_fst_data;
135 
136   std::array<u8, VolumeWii::AES_KEY_SIZE> m_key;
137 
138   std::string m_root_directory;
139   bool m_is_wii = false;
140   // GameCube has no shift, Wii has 2 bit shift
141   u32 m_address_shift = 0;
142 
143   u64 m_data_size;
144 };
145 
146 class DirectoryBlobReader : public BlobReader
147 {
148   friend DiscContent;
149 
150 public:
151   static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
152 
153   // We do not allow copying, because it might mess up the pointers inside DiscContents
154   DirectoryBlobReader(const DirectoryBlobReader&) = delete;
155   DirectoryBlobReader& operator=(const DirectoryBlobReader&) = delete;
156   DirectoryBlobReader(DirectoryBlobReader&&) = default;
157   DirectoryBlobReader& operator=(DirectoryBlobReader&&) = default;
158 
159   bool Read(u64 offset, u64 length, u8* buffer) override;
160   bool SupportsReadWiiDecrypted(u64 offset, u64 size, u64 partition_data_offset) const override;
161   bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_data_offset) override;
162 
163   BlobType GetBlobType() const override;
164 
165   u64 GetRawSize() const override;
166   u64 GetDataSize() const override;
IsDataSizeAccurate()167   bool IsDataSizeAccurate() const override { return true; }
168 
GetBlockSize()169   u64 GetBlockSize() const override { return 0; }
HasFastRandomAccessInBlock()170   bool HasFastRandomAccessInBlock() const override { return true; }
GetCompressionMethod()171   std::string GetCompressionMethod() const override { return {}; }
172 
173 private:
174   struct PartitionWithType
175   {
PartitionWithTypePartitionWithType176     PartitionWithType(DirectoryBlobPartition&& partition_, PartitionType type_)
177         : partition(std::move(partition_)), type(type_)
178     {
179     }
180 
181     DirectoryBlobPartition partition;
182     PartitionType type;
183   };
184 
185   explicit DirectoryBlobReader(const std::string& game_partition_root,
186                                const std::string& true_root);
187 
188   const DirectoryBlobPartition* GetPartition(u64 offset, u64 size, u64 partition_data_offset) const;
189 
190   bool EncryptPartitionData(u64 offset, u64 size, u8* buffer, u64 partition_data_offset,
191                             u64 partition_data_decrypted_size);
192 
193   void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
194                                  const std::string& game_partition_root);
195   void SetWiiRegionData(const std::string& game_partition_root);
196   void SetPartitions(std::vector<PartitionWithType>&& partitions);
197   void SetPartitionHeader(DirectoryBlobPartition* partition, u64 partition_address);
198 
199   // For GameCube:
200   DirectoryBlobPartition m_gamecube_pseudopartition;
201 
202   // For Wii:
203   DiscContentContainer m_nonpartition_contents;
204   std::map<u64, DirectoryBlobPartition> m_partitions;
205   WiiEncryptionCache m_encryption_cache;
206 
207   bool m_is_wii;
208   bool m_encrypted;
209 
210   std::vector<u8> m_disc_header_nonpartition;
211   std::vector<u8> m_partition_table;
212   std::vector<u8> m_wii_region_data;
213   std::vector<std::vector<u8>> m_partition_headers;
214 
215   u64 m_data_size;
216 };
217 
218 }  // namespace DiscIO
219