1use std::io::Read;
4
5use rakata_core::{decode_text_strict, ResRef, ResourceId, ResourceTypeCode};
6
7use super::{
8 binary, Key, KeyBifEntry, KeyBinaryError, KeyReadMode, KeyReadOptions, KeyResourceEntry,
9 FILE_ENTRY_SIZE, FILE_HEADER_SIZE, KEY_ENTRY_SIZE, KEY_MAGIC, KEY_TEXT_ENCODING,
10 KEY_VERSION_V10, KEY_VERSION_V11,
11};
12
13#[cfg_attr(
17 feature = "tracing",
18 tracing::instrument(level = "debug", skip(reader))
19)]
20pub fn read_key<R: Read>(reader: &mut R) -> Result<Key, KeyBinaryError> {
21 read_key_with_options(reader, KeyReadOptions::default())
22}
23
24#[cfg_attr(
26 feature = "tracing",
27 tracing::instrument(level = "debug", skip(reader))
28)]
29pub fn read_key_with_options<R: Read>(
30 reader: &mut R,
31 options: KeyReadOptions,
32) -> Result<Key, KeyBinaryError> {
33 let mut bytes = Vec::new();
34 reader.read_to_end(&mut bytes)?;
35 crate::trace_debug!(bytes_len = bytes.len(), "read key bytes from reader");
36 read_key_from_bytes_with_options(&bytes, options)
37}
38
39#[cfg_attr(
41 feature = "tracing",
42 tracing::instrument(level = "debug", skip(bytes), fields(bytes_len = bytes.len()))
43)]
44pub fn read_key_from_bytes(bytes: &[u8]) -> Result<Key, KeyBinaryError> {
45 read_key_from_bytes_with_options(bytes, KeyReadOptions::default())
46}
47
48#[cfg_attr(
50 feature = "tracing",
51 tracing::instrument(level = "debug", skip(bytes), fields(bytes_len = bytes.len()))
52)]
53pub fn read_key_from_bytes_with_options(
54 bytes: &[u8],
55 options: KeyReadOptions,
56) -> Result<Key, KeyBinaryError> {
57 if bytes.len() < FILE_HEADER_SIZE {
58 return Err(KeyBinaryError::InvalidHeader(
59 "file smaller than KEY header".into(),
60 ));
61 }
62
63 let magic = binary::read_fourcc(bytes, 0)?;
64 binary::expect_fourcc(magic, KEY_MAGIC).map_err(KeyBinaryError::InvalidMagic)?;
65
66 let version = binary::read_fourcc(bytes, 4)?;
67 match options.input {
68 KeyReadMode::CanonicalK1 => binary::expect_fourcc(version, KEY_VERSION_V10)
69 .map_err(KeyBinaryError::InvalidVersion)?,
70 KeyReadMode::CompatibilityAurora => {
71 binary::expect_any_fourcc(version, &[KEY_VERSION_V10, KEY_VERSION_V11])
72 .map_err(KeyBinaryError::InvalidVersion)?
73 }
74 }
75
76 let bif_count = binary::checked_to_usize(binary::read_u32(bytes, 8)?, "bif_count")?;
77 let key_count = binary::checked_to_usize(binary::read_u32(bytes, 12)?, "key_count")?;
78 let file_table_offset =
79 binary::checked_to_usize(binary::read_u32(bytes, 16)?, "file_table_offset")?;
80 let key_table_offset =
81 binary::checked_to_usize(binary::read_u32(bytes, 20)?, "key_table_offset")?;
82 let build_year = binary::read_u32(bytes, 24)?;
83 let build_day = binary::read_u32(bytes, 28)?;
84 let mut reserved = [0u8; 32];
85 if let Some(slice) = bytes.get(32..64) {
86 reserved.copy_from_slice(slice);
87 }
88
89 let file_table_size = bif_count
90 .checked_mul(FILE_ENTRY_SIZE)
91 .ok_or_else(|| KeyBinaryError::InvalidHeader("file table size overflow".into()))?;
92 binary::check_slice_in_bounds(bytes, file_table_offset, file_table_size, "file table")?;
93
94 let mut bif_entries = Vec::with_capacity(bif_count);
95 for bif_index in 0..bif_count {
96 let base = file_table_offset + bif_index * FILE_ENTRY_SIZE;
97 let file_size = binary::read_u32(bytes, base)?;
98 let filename_offset =
99 binary::checked_to_usize(binary::read_u32(bytes, base + 4)?, "filename_offset")?;
100 let filename_size = usize::from(binary::read_u16(bytes, base + 8)?);
101 let drives = binary::read_u16(bytes, base + 10)?;
102
103 binary::check_slice_in_bounds(
104 bytes,
105 filename_offset,
106 filename_size,
107 &format!("filename[{bif_index}]"),
108 )?;
109
110 let raw_filename = bytes
111 .get(filename_offset..filename_offset + filename_size)
112 .ok_or_else(|| {
113 KeyBinaryError::InvalidHeader(format!(
114 "filename bytes missing for entry {bif_index}"
115 ))
116 })?;
117 let filename_end = raw_filename
118 .iter()
119 .rposition(|byte| *byte != 0)
120 .map_or(0, |pos| pos + 1);
121 let filename = decode_text_strict(&raw_filename[..filename_end], KEY_TEXT_ENCODING)
122 .map_err(|source| KeyBinaryError::TextDecoding {
123 context: format!("bif_entries[{bif_index}].filename"),
124 source,
125 })?;
126
127 bif_entries.push(KeyBifEntry {
128 filename,
129 file_size,
130 drives,
131 });
132 }
133
134 let key_table_size = key_count
135 .checked_mul(KEY_ENTRY_SIZE)
136 .ok_or_else(|| KeyBinaryError::InvalidHeader("key table size overflow".into()))?;
137 binary::check_slice_in_bounds(bytes, key_table_offset, key_table_size, "key table")?;
138
139 let mut resources = Vec::with_capacity(key_count);
140 for key_index in 0..key_count {
141 let base = key_table_offset + key_index * KEY_ENTRY_SIZE;
142 let raw_resref = bytes
143 .get(base..base + 16)
144 .ok_or_else(|| KeyBinaryError::InvalidData("resref bytes missing".into()))?;
145 let resref_end = raw_resref.iter().position(|byte| *byte == 0).unwrap_or(16);
146 let resref_str =
147 decode_text_strict(&raw_resref[..resref_end], KEY_TEXT_ENCODING).map_err(|source| {
148 KeyBinaryError::TextDecoding {
149 context: format!("resources[{key_index}].resref"),
150 source,
151 }
152 })?;
153 let resref = ResRef::new(&resref_str).map_err(|source| KeyBinaryError::InvalidResRef {
154 context: format!("resources[{key_index}].resref"),
155 source,
156 })?;
157 let resource_type = ResourceTypeCode::from_raw_id(binary::read_u16(bytes, base + 16)?);
158 let resource_id = ResourceId::from_raw(binary::read_u32(bytes, base + 18)?);
159
160 resources.push(KeyResourceEntry {
161 resref,
162 resource_type,
163 resource_id,
164 });
165 }
166
167 let key = Key {
168 build_year,
169 build_day,
170 bif_entries,
171 resources,
172 reserved,
173 };
174 crate::trace_debug!(
175 bif_count = key.bif_entries.len(),
176 resource_count = key.resources.len(),
177 "parsed key from bytes"
178 );
179 Ok(key)
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::key::{pack_resource_id, write_key_to_vec, KeyReadMode};
186 use rakata_core::ResourceId;
187
188 #[test]
189 fn roundtrip_synthetic_key() {
190 let mut key = Key::new();
191 key.build_year = 123;
192 key.build_day = 45;
193 key.push_bif_entry("data/models.bif", 111_222, 1);
194 key.push_bif_entry("data/textures.bif", 333_444, 1);
195 key.push_resource(
196 ResRef::new("m13aa").unwrap(),
197 ResourceTypeCode::from_raw_id(2014),
198 pack_resource_id(0, 1).expect("pack id").into(),
199 );
200 key.push_resource(
201 ResRef::new("m13ab").unwrap(),
202 ResourceTypeCode::from_raw_id(2016),
203 pack_resource_id(0, 2).expect("pack id").into(),
204 );
205 key.push_resource(
206 ResRef::new("lbl_map").unwrap(),
207 ResourceTypeCode::from_raw_id(2017),
208 pack_resource_id(1, 7).expect("pack id").into(),
209 );
210
211 let bytes = write_key_to_vec(&key).expect("write should succeed");
212 let parsed = read_key_from_bytes(&bytes).expect("read should succeed");
213
214 assert_eq!(parsed, key);
215 }
216
217 #[test]
218 fn writer_is_deterministic_for_synthetic_key() {
219 let mut key = Key::new();
220 key.build_year = 123;
221 key.build_day = 45;
222 key.push_bif_entry("data/models.bif", 111_222, 1);
223 key.push_bif_entry("data/textures.bif", 333_444, 1);
224 key.push_resource(
225 ResRef::new("m13aa").unwrap(),
226 ResourceTypeCode::from_raw_id(2014),
227 pack_resource_id(0, 1).expect("pack id").into(),
228 );
229 key.push_resource(
230 ResRef::new("m13ab").unwrap(),
231 ResourceTypeCode::from_raw_id(2016),
232 pack_resource_id(0, 2).expect("pack id").into(),
233 );
234 key.push_resource(
235 ResRef::new("lbl_map").unwrap(),
236 ResourceTypeCode::from_raw_id(2017),
237 pack_resource_id(1, 7).expect("pack id").into(),
238 );
239
240 let first = write_key_to_vec(&key).expect("first write should succeed");
241 let second = write_key_to_vec(&key).expect("second write should succeed");
242 assert_eq!(first, second, "canonical KEY writer output drifted");
243 }
244
245 #[test]
246 fn roundtrip_preserves_unknown_resource_type_ids() {
247 let mut key = Key::new();
248 key.push_bif_entry("data/custom.bif", 42, 0);
249 key.push_resource(
250 ResRef::new("mystery").unwrap(),
251 ResourceTypeCode::from_raw_id(42424),
252 pack_resource_id(0, 5).expect("pack id").into(),
253 );
254
255 let bytes = write_key_to_vec(&key).expect("write should succeed");
256 let parsed = read_key_from_bytes(&bytes).expect("read should succeed");
257
258 assert_eq!(parsed.resources.len(), 1);
259 assert_eq!(parsed.resources[0].resource_type.raw_id(), 42424);
260 assert_eq!(parsed.resources[0].resource_type.known_type(), None);
261 }
262
263 #[test]
264 fn resource_id_helpers_work() {
265 let entry = KeyResourceEntry::from_indices(
266 ResRef::new("alpha").expect("valid resref"),
267 ResourceTypeCode::from_raw_id(2017),
268 0xabc,
269 0x54321,
270 )
271 .expect("indices should fit");
272
273 assert_eq!(entry.bif_index(), 0xabc);
274 assert_eq!(entry.resource_index(), 0x54321);
275 }
276
277 #[test]
278 fn rejects_invalid_resource_id_parts() {
279 let err = pack_resource_id(0x1000, 0).expect_err("must fail");
280 assert!(matches!(err, KeyBinaryError::InvalidResourceIdParts { .. }));
281
282 let err = pack_resource_id(0, 0x10_0000).expect_err("must fail");
283 assert!(matches!(err, KeyBinaryError::InvalidResourceIdParts { .. }));
284 }
285
286 #[test]
287 fn compatibility_reader_accepts_v11_key_version() {
288 let mut bytes = vec![0_u8; FILE_HEADER_SIZE];
289 bytes[0..4].copy_from_slice(&KEY_MAGIC);
290 bytes[4..8].copy_from_slice(&KEY_VERSION_V11);
291 bytes[16..20].copy_from_slice(
292 &u32::try_from(FILE_HEADER_SIZE)
293 .expect("FILE_HEADER_SIZE fits in u32")
294 .to_le_bytes(),
295 );
296 bytes[20..24].copy_from_slice(
297 &u32::try_from(FILE_HEADER_SIZE)
298 .expect("FILE_HEADER_SIZE fits in u32")
299 .to_le_bytes(),
300 );
301
302 let key = read_key_from_bytes_with_options(
303 &bytes,
304 KeyReadOptions {
305 input: KeyReadMode::CompatibilityAurora,
306 },
307 )
308 .expect("v1.1 should parse");
309 assert_eq!(key.bif_entries.len(), 0);
310 assert_eq!(key.resources.len(), 0);
311 }
312
313 #[test]
314 fn canonical_reader_rejects_v11_key_version() {
315 let mut bytes = vec![0_u8; FILE_HEADER_SIZE];
316 bytes[0..4].copy_from_slice(&KEY_MAGIC);
317 bytes[4..8].copy_from_slice(&KEY_VERSION_V11);
318 bytes[16..20].copy_from_slice(
319 &u32::try_from(FILE_HEADER_SIZE)
320 .expect("FILE_HEADER_SIZE fits in u32")
321 .to_le_bytes(),
322 );
323 bytes[20..24].copy_from_slice(
324 &u32::try_from(FILE_HEADER_SIZE)
325 .expect("FILE_HEADER_SIZE fits in u32")
326 .to_le_bytes(),
327 );
328
329 let err = read_key_from_bytes(&bytes).expect_err("canonical mode must fail");
330 assert!(matches!(err, KeyBinaryError::InvalidVersion(_)));
331 }
332
333 #[test]
334 fn rejects_invalid_magic() {
335 let mut bytes = vec![0_u8; FILE_HEADER_SIZE];
336 bytes[0..4].copy_from_slice(b"NOPE");
337 bytes[4..8].copy_from_slice(&KEY_VERSION_V10);
338 let err = read_key_from_bytes(&bytes).expect_err("must fail");
339 assert!(matches!(err, KeyBinaryError::InvalidMagic(_)));
340 }
341
342 #[test]
343 fn rejects_invalid_version() {
344 let mut bytes = vec![0_u8; FILE_HEADER_SIZE];
345 bytes[0..4].copy_from_slice(&KEY_MAGIC);
346 bytes[4..8].copy_from_slice(b"V9.9");
347 let err = read_key_from_bytes(&bytes).expect_err("must fail");
348 assert!(matches!(err, KeyBinaryError::InvalidVersion(_)));
349 }
350
351 #[test]
352 fn rejects_truncated_header() {
353 let bytes = vec![0_u8; FILE_HEADER_SIZE - 1];
354 let err = read_key_from_bytes(&bytes).expect_err("must fail");
355 assert!(matches!(err, KeyBinaryError::InvalidHeader(_)));
356 }
357
358 #[test]
359 fn rejects_out_of_bounds_filename_offset() {
360 let mut bytes = vec![0_u8; FILE_HEADER_SIZE + FILE_ENTRY_SIZE];
361 bytes[0..4].copy_from_slice(&KEY_MAGIC);
362 bytes[4..8].copy_from_slice(&KEY_VERSION_V10);
363 bytes[8..12].copy_from_slice(&1_u32.to_le_bytes());
364 bytes[16..20].copy_from_slice(
365 &u32::try_from(FILE_HEADER_SIZE)
366 .expect("FILE_HEADER_SIZE fits in u32")
367 .to_le_bytes(),
368 );
369 bytes[20..24].copy_from_slice(
370 &u32::try_from(FILE_HEADER_SIZE + FILE_ENTRY_SIZE)
371 .expect("header + entry size fits in u32")
372 .to_le_bytes(),
373 );
374
375 let base = FILE_HEADER_SIZE;
377 bytes[base + 4..base + 8].copy_from_slice(&999_u32.to_le_bytes());
378 bytes[base + 8..base + 10].copy_from_slice(&10_u16.to_le_bytes());
379
380 let err = read_key_from_bytes(&bytes).expect_err("must fail");
381 assert!(matches!(err, KeyBinaryError::InvalidHeader(_)));
382 }
383
384 #[test]
385 fn resref_validation_rejects_long_names() {
386 let result = ResRef::new("resref_is_way_too_long");
388 assert!(result.is_err());
389 }
390
391 #[test]
392 fn resource_lookup_by_packed_id_accepts_raw_and_typed_values() {
393 let mut key = Key::new();
394 let packed = ResourceId::from_parts(2, 7).expect("valid packed id");
395 key.push_resource(
396 ResRef::new("alpha").unwrap(),
397 ResourceTypeCode::from_raw_id(2017),
398 packed,
399 );
400
401 assert!(key.resource_by_id(packed).is_some());
402 assert!(key
403 .resource_by_id(rakata_core::ResourceId::from_raw(packed.raw()))
404 .is_some());
405 }
406
407 #[test]
408 fn pack_resource_id_accepts_boundary_values() {
409 let packed = pack_resource_id(0x0fff, 0x0f_ffff).expect("max parts should pack");
410 let resource_id = ResourceId::from_raw(packed);
411
412 assert_eq!(resource_id.bif_index(), 0x0fff);
413 assert_eq!(resource_id.resource_index(), 0x0f_ffff);
414 }
415
416 #[test]
417 fn duplicate_key_entries_keep_order_and_first_lookup() {
418 let mut key = Key::new();
419 key.push_bif_entry("data/test.bif", 10, 0);
420 let first_id = ResourceId::from_parts(0, 1).expect("valid id");
421 let second_id = ResourceId::from_parts(0, 2).expect("valid id");
422 key.push_resource(
423 ResRef::new("dup_res").unwrap(),
424 ResourceTypeCode::from_raw_id(2017),
425 first_id,
426 );
427 key.push_resource(
428 ResRef::new("dup_res").unwrap(),
429 ResourceTypeCode::from_raw_id(2017),
430 second_id,
431 );
432
433 let bytes = write_key_to_vec(&key).expect("write should succeed");
434 let parsed = read_key_from_bytes(&bytes).expect("read should succeed");
435
436 assert_eq!(parsed.resources.len(), 2);
437 assert_eq!(parsed.resources[0].resource_id, first_id);
438 assert_eq!(parsed.resources[1].resource_id, second_id);
439 assert_eq!(
440 parsed
441 .resource(
442 &ResRef::new("dup_res").unwrap(),
443 ResourceTypeCode::from_raw_id(2017)
444 )
445 .expect("duplicate key should resolve"),
446 &parsed.resources[0]
447 );
448 }
449
450 #[test]
451 fn reserved_bytes_survive_roundtrip() {
452 let mut key = Key::new();
453 key.reserved[0] = 0xAB;
454 key.reserved[15] = 0xCD;
455 key.reserved[31] = 0xEF;
456
457 let bytes = write_key_to_vec(&key).expect("write should succeed");
458 let parsed = read_key_from_bytes(&bytes).expect("read should succeed");
459
460 assert_eq!(parsed.reserved[0], 0xAB);
461 assert_eq!(parsed.reserved[15], 0xCD);
462 assert_eq!(parsed.reserved[31], 0xEF);
463 }
464}