@@ -3,7 +3,7 @@ use std::{
3
3
io:: { BufRead , Read , Seek , SeekFrom } ,
4
4
} ;
5
5
6
- use zerocopy:: FromZeros ;
6
+ use zerocopy:: { FromBytes , FromZeros } ;
7
7
8
8
use super :: {
9
9
gcn:: PartitionGC ,
@@ -16,15 +16,10 @@ use crate::{
16
16
disc:: wii:: REGION_OFFSET ,
17
17
io:: block:: { Block , BlockIO , PartitionInfo } ,
18
18
util:: read:: { read_box, read_from, read_vec} ,
19
- DiscMeta , Error , OpenOptions , Result , ResultContext , SECTOR_SIZE ,
19
+ DiscMeta , Error , OpenOptions , PartitionEncryptionMode , PartitionOptions , Result , ResultContext ,
20
+ SECTOR_SIZE ,
20
21
} ;
21
22
22
- #[ derive( Debug , Eq , PartialEq , Copy , Clone ) ]
23
- pub enum EncryptionMode {
24
- Encrypted ,
25
- Decrypted ,
26
- }
27
-
28
23
pub struct DiscReader {
29
24
io : Box < dyn BlockIO > ,
30
25
block : Block ,
@@ -33,7 +28,7 @@ pub struct DiscReader {
33
28
sector_buf : Box < [ u8 ; SECTOR_SIZE ] > ,
34
29
sector_idx : u32 ,
35
30
pos : u64 ,
36
- mode : EncryptionMode ,
31
+ mode : PartitionEncryptionMode ,
37
32
disc_header : Box < DiscHeader > ,
38
33
pub ( crate ) partitions : Vec < PartitionInfo > ,
39
34
hash_tables : Vec < HashTable > ,
@@ -71,11 +66,7 @@ impl DiscReader {
71
66
sector_buf : <[ u8 ; SECTOR_SIZE ] >:: new_box_zeroed ( ) ?,
72
67
sector_idx : u32:: MAX ,
73
68
pos : 0 ,
74
- mode : if options. rebuild_encryption {
75
- EncryptionMode :: Encrypted
76
- } else {
77
- EncryptionMode :: Decrypted
78
- } ,
69
+ mode : options. partition_encryption ,
79
70
disc_header : DiscHeader :: new_box_zeroed ( ) ?,
80
71
partitions : vec ! [ ] ,
81
72
hash_tables : vec ! [ ] ,
@@ -84,11 +75,28 @@ impl DiscReader {
84
75
let disc_header: Box < DiscHeader > = read_box ( & mut reader) . context ( "Reading disc header" ) ?;
85
76
reader. disc_header = disc_header;
86
77
if reader. disc_header . is_wii ( ) {
78
+ if reader. disc_header . has_partition_encryption ( )
79
+ && !reader. disc_header . has_partition_hashes ( )
80
+ {
81
+ return Err ( Error :: DiscFormat (
82
+ "Wii disc is encrypted but has no partition hashes" . to_string ( ) ,
83
+ ) ) ;
84
+ }
85
+ if !reader. disc_header . has_partition_hashes ( )
86
+ && options. partition_encryption == PartitionEncryptionMode :: ForceEncrypted
87
+ {
88
+ return Err ( Error :: Other (
89
+ "Unsupported: Rebuilding encryption for Wii disc without hashes" . to_string ( ) ,
90
+ ) ) ;
91
+ }
87
92
reader. seek ( SeekFrom :: Start ( REGION_OFFSET ) ) . context ( "Seeking to region info" ) ?;
88
93
reader. region = Some ( read_from ( & mut reader) . context ( "Reading region info" ) ?) ;
89
94
reader. partitions = read_partition_info ( & mut reader) ?;
90
95
// Rebuild hashes if the format requires it
91
- if ( options. rebuild_encryption || options. validate_hashes ) && meta. needs_hash_recovery {
96
+ if options. partition_encryption != PartitionEncryptionMode :: AsIs
97
+ && meta. needs_hash_recovery
98
+ && reader. disc_header . has_partition_hashes ( )
99
+ {
92
100
rebuild_hashes ( & mut reader) ?;
93
101
}
94
102
}
@@ -125,7 +133,7 @@ impl DiscReader {
125
133
pub fn open_partition (
126
134
& self ,
127
135
index : usize ,
128
- options : & OpenOptions ,
136
+ options : & PartitionOptions ,
129
137
) -> Result < Box < dyn PartitionBase > > {
130
138
if self . disc_header . is_gamecube ( ) {
131
139
if index == 0 {
@@ -145,7 +153,7 @@ impl DiscReader {
145
153
pub fn open_partition_kind (
146
154
& self ,
147
155
kind : PartitionKind ,
148
- options : & OpenOptions ,
156
+ options : & PartitionOptions ,
149
157
) -> Result < Box < dyn PartitionBase > > {
150
158
if self . disc_header . is_gamecube ( ) {
151
159
if kind == PartitionKind :: Data {
@@ -182,30 +190,51 @@ impl BufRead for DiscReader {
182
190
183
191
// Read new sector into buffer
184
192
if abs_sector != self . sector_idx {
185
- if let Some ( partition) = partition {
186
- match self . mode {
187
- EncryptionMode :: Decrypted => self . block . decrypt (
193
+ match ( self . mode , partition, self . disc_header . has_partition_encryption ( ) ) {
194
+ ( PartitionEncryptionMode :: Original , Some ( partition) , true )
195
+ | ( PartitionEncryptionMode :: ForceEncrypted , Some ( partition) , _) => {
196
+ self . block . encrypt (
188
197
self . sector_buf . as_mut ( ) ,
189
198
self . block_buf . as_ref ( ) ,
190
199
abs_sector,
191
200
partition,
192
- ) ?,
193
- EncryptionMode :: Encrypted => self . block . encrypt (
201
+ ) ?;
202
+ }
203
+ ( PartitionEncryptionMode :: ForceDecrypted , Some ( partition) , _) => {
204
+ self . block . decrypt (
194
205
self . sector_buf . as_mut ( ) ,
195
206
self . block_buf . as_ref ( ) ,
196
207
abs_sector,
197
208
partition,
198
- ) ?,
209
+ ) ?;
210
+ }
211
+ ( PartitionEncryptionMode :: AsIs , _, _) | ( _, None , _) | ( _, _, false ) => {
212
+ self . block . copy_raw (
213
+ self . sector_buf . as_mut ( ) ,
214
+ self . block_buf . as_ref ( ) ,
215
+ abs_sector,
216
+ & self . disc_header ,
217
+ ) ?;
199
218
}
200
- } else {
201
- self . block . copy_raw (
202
- self . sector_buf . as_mut ( ) ,
203
- self . block_buf . as_ref ( ) ,
204
- abs_sector,
205
- & self . disc_header ,
206
- ) ?;
207
219
}
208
220
self . sector_idx = abs_sector;
221
+
222
+ if self . sector_idx == 0
223
+ && self . disc_header . is_wii ( )
224
+ && matches ! (
225
+ self . mode,
226
+ PartitionEncryptionMode :: ForceDecrypted
227
+ | PartitionEncryptionMode :: ForceEncrypted
228
+ )
229
+ {
230
+ let ( disc_header, _) = DiscHeader :: mut_from_prefix ( self . sector_buf . as_mut ( ) )
231
+ . expect ( "Invalid disc header alignment" ) ;
232
+ disc_header. no_partition_encryption = match self . mode {
233
+ PartitionEncryptionMode :: ForceDecrypted => 1 ,
234
+ PartitionEncryptionMode :: ForceEncrypted => 0 ,
235
+ _ => unreachable ! ( ) ,
236
+ } ;
237
+ }
209
238
}
210
239
211
240
// Read from sector buffer
@@ -273,8 +302,19 @@ fn read_partition_info(reader: &mut DiscReader) -> Result<Vec<PartitionInfo>> {
273
302
"Partition {group_idx}:{part_idx} offset is not sector aligned" ,
274
303
) ) ) ;
275
304
}
305
+
306
+ let disc_header = reader. header ( ) ;
276
307
let data_start_offset = entry. offset ( ) + header. data_off ( ) ;
277
- let data_end_offset = data_start_offset + header. data_size ( ) ;
308
+ let mut data_size = header. data_size ( ) ;
309
+ if data_size == 0 {
310
+ // Read until next partition or end of disc
311
+ // TODO: handle multiple partition groups
312
+ data_size = entries
313
+ . get ( part_idx + 1 )
314
+ . map ( |part| part. offset ( ) - data_start_offset)
315
+ . unwrap_or ( reader. disc_size ( ) - data_start_offset) ;
316
+ }
317
+ let data_end_offset = data_start_offset + data_size;
278
318
if data_start_offset % SECTOR_SIZE as u64 != 0
279
319
|| data_end_offset % SECTOR_SIZE as u64 != 0
280
320
{
@@ -293,13 +333,15 @@ fn read_partition_info(reader: &mut DiscReader) -> Result<Vec<PartitionInfo>> {
293
333
disc_header : DiscHeader :: new_box_zeroed ( ) ?,
294
334
partition_header : PartitionHeader :: new_box_zeroed ( ) ?,
295
335
hash_table : None ,
336
+ has_encryption : disc_header. has_partition_encryption ( ) ,
337
+ has_hashes : disc_header. has_partition_hashes ( ) ,
296
338
} ;
297
339
298
340
let mut partition_reader = PartitionWii :: new (
299
341
reader. io . clone ( ) ,
300
342
reader. disc_header . clone ( ) ,
301
343
& info,
302
- & OpenOptions :: default ( ) ,
344
+ & PartitionOptions { validate_hashes : false } ,
303
345
) ?;
304
346
info. disc_header = read_box ( & mut partition_reader) . context ( "Reading disc header" ) ?;
305
347
info. partition_header =
0 commit comments