1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <variant>
#include <vector>
#include "Common/CommonTypes.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Volume.h"
#include "DiscIO/WiiEncryptionCache.h"
namespace File
{
struct FSTEntry;
} // namespace File
namespace DiscIO
{
enum class PartitionType : u32;
class DirectoryBlobReader;
class VolumeDisc;
// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
bool ShouldHideFromGameList(const std::string& volume_path);
// Content chunk that is loaded from a file in the host file system.
struct ContentFile
{
// Path where the file can be found.
std::string m_filename;
// Offset from the start of the file where the first byte of this content chunk is.
u64 m_offset = 0;
};
// Content chunk that's just a direct block of memory
typedef std::shared_ptr<std::vector<u8>> ContentMemory;
// Content chunk that loads data from a DirectoryBlobReader.
// Intended for representing a partition within a disc.
struct ContentPartition
{
// Offset from the start of the partition for the first byte represented by this chunk.
u64 m_offset = 0;
// The value passed as partition_data_offset to EncryptPartitionData().
u64 m_partition_data_offset = 0;
};
// Content chunk that loads data from a Volume.
struct ContentVolume
{
// Offset from the start of the volume for the first byte represented by this chunk.
u64 m_offset = 0;
// The partition passed to the Volume's Read() method.
Partition m_partition;
};
// Content chunk representing a run of identical bytes.
// Useful for padding between chunks within a file.
struct ContentFixedByte
{
u8 m_byte = 0;
};
using ContentSource = std::variant<ContentFile, // File
ContentMemory, // Memory/Byte Sequence
ContentPartition, // Partition
ContentVolume, // Volume
ContentFixedByte // Fixed value padding
>;
struct BuilderContentSource
{
u64 m_offset = 0;
u64 m_size = 0;
ContentSource m_source;
};
struct FSTBuilderNode
{
std::string m_filename;
u64 m_size = 0;
std::variant<std::vector<BuilderContentSource>, std::vector<FSTBuilderNode>> m_content;
void* m_user_data = nullptr;
bool IsFile() const
{
return std::holds_alternative<std::vector<BuilderContentSource>>(m_content);
}
std::vector<BuilderContentSource>& GetFileContent()
{
return std::get<std::vector<BuilderContentSource>>(m_content);
}
const std::vector<BuilderContentSource>& GetFileContent() const
{
return std::get<std::vector<BuilderContentSource>>(m_content);
}
bool IsFolder() const { return std::holds_alternative<std::vector<FSTBuilderNode>>(m_content); }
std::vector<FSTBuilderNode>& GetFolderContent()
{
return std::get<std::vector<FSTBuilderNode>>(m_content);
}
const std::vector<FSTBuilderNode>& GetFolderContent() const
{
return std::get<std::vector<FSTBuilderNode>>(m_content);
}
};
class DiscContent
{
public:
DiscContent(u64 offset, u64 size, ContentSource source);
// Provided because it's convenient when searching for DiscContent in an std::set
explicit DiscContent(u64 offset);
u64 GetOffset() const;
u64 GetEndOffset() const;
u64 GetSize() const;
bool Read(u64* offset, u64* length, u8** buffer, DirectoryBlobReader* blob) const;
bool operator==(const DiscContent& other) const { return GetEndOffset() == other.GetEndOffset(); }
bool operator<(const DiscContent& other) const { return GetEndOffset() < other.GetEndOffset(); }
bool operator>(const DiscContent& other) const { return other < *this; }
bool operator<=(const DiscContent& other) const { return !(*this > other); }
bool operator>=(const DiscContent& other) const { return !(*this < other); }
private:
// Position of this content chunk within its parent DiscContentContainer.
u64 m_offset;
// Number of bytes this content chunk takes up.
u64 m_size = 0;
// Where and how to find the data for this content chunk.
ContentSource m_content_source;
};
class DiscContentContainer
{
public:
void Add(u64 offset, std::vector<u8> vector)
{
size_t vector_size = vector.size();
return Add(offset, vector_size, std::make_shared<std::vector<u8>>(std::move(vector)));
}
void Add(u64 offset, u64 size, ContentSource source);
u64 CheckSizeAndAdd(u64 offset, const std::string& path);
u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);
bool Read(u64 offset, u64 length, u8* buffer, DirectoryBlobReader* blob) const;
private:
std::set<DiscContent> m_contents;
};
class DirectoryBlobPartition
{
public:
DirectoryBlobPartition() = default;
DirectoryBlobPartition(std::string root_directory, std::optional<bool> is_wii);
DirectoryBlobPartition(
VolumeDisc* volume, const Partition& partition, std::optional<bool> is_wii,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes)>& sys_callback,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes, FSTBuilderNode* dol_node)>&
fst_callback,
DirectoryBlobReader* blob);
DirectoryBlobPartition(const DirectoryBlobPartition&) = default;
DirectoryBlobPartition& operator=(const DirectoryBlobPartition&) = default;
DirectoryBlobPartition(DirectoryBlobPartition&&) = default;
DirectoryBlobPartition& operator=(DirectoryBlobPartition&&) = default;
bool IsWii() const { return m_is_wii; }
u64 GetDataSize() const { return m_data_size; }
void SetDataSize(u64 size) { m_data_size = size; }
const std::string& GetRootDirectory() const { return m_root_directory; }
const DiscContentContainer& GetContents() const { return m_contents; }
const std::optional<Partition>& GetWrappedPartition() const { return m_wrapped_partition; }
const std::array<u8, VolumeWii::AES_KEY_SIZE>& GetKey() const { return m_key; }
void SetKey(std::array<u8, VolumeWii::AES_KEY_SIZE> key) { m_key = key; }
private:
void SetDiscType(std::optional<bool> is_wii, std::span<const u8> disc_header);
void SetBI2FromFile(const std::string& bi2_path);
void SetBI2(std::vector<u8> bi2);
// Returns DOL address
u64 SetApploaderFromFile(const std::string& path);
u64 SetApploader(std::vector<u8> apploader, const std::string& log_path);
// Returns FST address
u64 SetDOLFromFile(const std::string& path, u64 dol_address, std::vector<u8>* disc_header);
u64 SetDOL(FSTBuilderNode dol_node, u64 dol_address, std::vector<u8>* disc_header);
void BuildFSTFromFolder(const std::string& fst_root_path, u64 fst_address,
std::vector<u8>* disc_header);
void BuildFST(std::vector<FSTBuilderNode> root_nodes, u64 fst_address,
std::vector<u8>* disc_header);
// FST creation
void WriteEntryData(std::vector<u8>* fst_data, u32* entry_offset, u8 type, u32 name_offset,
u64 data_offset, u64 length, u32 address_shift);
void WriteEntryName(std::vector<u8>* fst_data, u32* name_offset, const std::string& name,
u64 name_table_offset);
void WriteDirectory(std::vector<u8>* fst_data, std::vector<FSTBuilderNode>* parent_entries,
u32* fst_offset, u32* name_offset, u64* data_offset, u32 parent_entry_index,
u64 name_table_offset);
DiscContentContainer m_contents;
std::array<u8, VolumeWii::AES_KEY_SIZE> m_key{};
std::string m_root_directory;
bool m_is_wii = false;
bool m_is_triforce = false;
// GameCube has no shift, Wii has 2 bit shift
u32 m_address_shift = 0;
u64 m_data_size = 0;
std::optional<Partition> m_wrapped_partition = std::nullopt;
};
class DirectoryBlobReader final : public BlobReader
{
friend DiscContent;
public:
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
static std::unique_ptr<DirectoryBlobReader> Create(
std::unique_ptr<VolumeDisc> volume,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes)>& sys_callback,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes, FSTBuilderNode* dol_node)>&
fst_callback);
DirectoryBlobReader(DirectoryBlobReader&&) = default;
DirectoryBlobReader& operator=(DirectoryBlobReader&&) = default;
bool Read(u64 offset, u64 length, u8* buffer) override;
bool SupportsReadWiiDecrypted(u64 offset, u64 size, u64 partition_data_offset) const override;
bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_data_offset) override;
BlobType GetBlobType() const override;
std::unique_ptr<BlobReader> CopyReader() const override;
u64 GetRawSize() const override;
u64 GetDataSize() const override;
DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; }
u64 GetBlockSize() const override { return 0; }
bool HasFastRandomAccessInBlock() const override { return true; }
std::string GetCompressionMethod() const override { return {}; }
std::optional<int> GetCompressionLevel() const override { return std::nullopt; }
private:
struct PartitionWithType
{
PartitionWithType(DirectoryBlobPartition&& partition_, PartitionType type_)
: partition(std::move(partition_)), type(type_)
{
}
DirectoryBlobPartition partition;
PartitionType type;
};
explicit DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root);
explicit DirectoryBlobReader(
std::unique_ptr<VolumeDisc> volume,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes)>& sys_callback,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes, FSTBuilderNode* dol_node)>&
fst_callback);
explicit DirectoryBlobReader(const DirectoryBlobReader& rhs);
const DirectoryBlobPartition* GetPartition(u64 offset, u64 size, u64 partition_data_offset) const;
bool EncryptPartitionData(u64 offset, u64 size, u8* buffer, u64 partition_data_offset,
u64 partition_data_decrypted_size);
void SetNonpartitionDiscHeaderFromFile(std::span<const u8> partition_header,
const std::string& game_partition_root);
void SetNonpartitionDiscHeader(std::span<const u8> partition_header, std::vector<u8> header_bin);
void SetWiiRegionDataFromFile(const std::string& game_partition_root);
void SetWiiRegionData(std::span<const u8> wii_region_data, const std::string& log_path);
void SetPartitions(std::vector<PartitionWithType>&& partitions);
void SetPartitionHeader(DirectoryBlobPartition* partition, u64 partition_address);
const VolumeDisc* GetWrappedVolume() const { return m_wrapped_volume.get(); }
// For GameCube:
DirectoryBlobPartition m_gamecube_pseudopartition;
// For Wii:
DiscContentContainer m_nonpartition_contents;
std::map<u64, DirectoryBlobPartition> m_partitions;
WiiEncryptionCache m_encryption_cache;
bool m_is_wii;
bool m_encrypted;
u64 m_data_size;
std::unique_ptr<VolumeDisc> m_wrapped_volume;
};
} // namespace DiscIO
|