1use std::{
2 collections::hash_map::RandomState,
3 fmt, io,
4 sync::{Arc, Mutex},
5 time::SystemTime,
6};
7
8use indexmap::IndexMap;
9use nwnrs_checksums::prelude::*;
10use nwnrs_compressedbuf::prelude::*;
11use nwnrs_exo::prelude::*;
12use nwnrs_resman::prelude::*;
13use nwnrs_resref::prelude::*;
14
15pub(crate) const HEADER_SIZE: u64 = 64;
16
17pub type ResId = u32;
22pub type BifResolver =
24 Arc<dyn Fn(&str) -> io::Result<Option<SharedReadSeek>> + Send + Sync + 'static>;
25
26#[derive(#[automatically_derived]
impl ::core::fmt::Debug for KeyError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
KeyError::Io(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Io",
&__self_0),
KeyError::ResMan(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "ResMan",
&__self_0),
KeyError::ResRef(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "ResRef",
&__self_0),
KeyError::Compression(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Compression", &__self_0),
KeyError::Message(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Message", &__self_0),
}
}
}Debug)]
27pub enum KeyError {
29 Io(io::Error),
31 ResMan(ResManError),
33 ResRef(ResRefError),
35 Compression(CompressedBufError),
37 Message(String),
39}
40
41impl KeyError {
42 pub(crate) fn msg(message: impl Into<String>) -> Self {
43 Self::Message(message.into())
44 }
45}
46
47impl fmt::Display for KeyError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::Io(error) => error.fmt(f),
51 Self::ResMan(error) => error.fmt(f),
52 Self::ResRef(error) => error.fmt(f),
53 Self::Compression(error) => error.fmt(f),
54 Self::Message(message) => f.write_str(message),
55 }
56 }
57}
58
59impl std::error::Error for KeyError {}
60
61impl From<io::Error> for KeyError {
62 fn from(value: io::Error) -> Self {
63 Self::Io(value)
64 }
65}
66
67impl From<ResManError> for KeyError {
68 fn from(value: ResManError) -> Self {
69 Self::ResMan(value)
70 }
71}
72
73impl From<ResRefError> for KeyError {
74 fn from(value: ResRefError) -> Self {
75 Self::ResRef(value)
76 }
77}
78
79impl From<CompressedBufError> for KeyError {
80 fn from(value: CompressedBufError) -> Self {
81 Self::Compression(value)
82 }
83}
84
85pub type KeyResult<T> = Result<T, KeyError>;
87
88#[derive(#[automatically_derived]
impl ::core::fmt::Debug for KeyBifVersion {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
KeyBifVersion::V1 => "V1",
KeyBifVersion::E1 => "E1",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for KeyBifVersion {
#[inline]
fn clone(&self) -> KeyBifVersion { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for KeyBifVersion { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for KeyBifVersion {
#[inline]
fn eq(&self, other: &KeyBifVersion) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for KeyBifVersion {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
89pub enum KeyBifVersion {
91 V1,
93 E1,
96}
97
98#[derive(#[automatically_derived]
impl ::core::fmt::Debug for VariableResource {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field5_finish(f,
"VariableResource", "id", &self.id, "io_offset", &self.io_offset,
"io_size", &self.io_size, "compression_type",
&self.compression_type, "uncompressed_size",
&&self.uncompressed_size)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for VariableResource {
#[inline]
fn clone(&self) -> VariableResource {
VariableResource {
id: ::core::clone::Clone::clone(&self.id),
io_offset: ::core::clone::Clone::clone(&self.io_offset),
io_size: ::core::clone::Clone::clone(&self.io_size),
compression_type: ::core::clone::Clone::clone(&self.compression_type),
uncompressed_size: ::core::clone::Clone::clone(&self.uncompressed_size),
}
}
}Clone)]
99pub struct VariableResource {
101 pub(crate) id: ResId,
102 pub(crate) io_offset: u64,
103 pub(crate) io_size: usize,
104 pub(crate) compression_type: ExoResFileCompressionType,
105 pub(crate) uncompressed_size: usize,
106}
107
108impl VariableResource {
109 #[must_use]
111 pub fn id(&self) -> ResId {
112 self.id
113 }
114
115 #[must_use]
117 pub fn io_offset(&self) -> u64 {
118 self.io_offset
119 }
120
121 #[must_use]
123 pub fn io_size(&self) -> usize {
124 self.io_size
125 }
126
127 #[must_use]
129 pub fn compression_type(&self) -> ExoResFileCompressionType {
130 self.compression_type
131 }
132
133 #[must_use]
135 pub fn uncompressed_size(&self) -> usize {
136 self.uncompressed_size
137 }
138}
139
140pub(crate) struct LoadedBif {
141 pub stream: SharedReadSeek,
142 pub file_type: String,
143 pub file_version: KeyBifVersion,
144 pub variable_resources: IndexMap<ResId, VariableResource, RandomState>,
145 pub oid: Option<String>,
146 pub raw_oid: Option<String>,
147}
148
149impl fmt::Debug for LoadedBif {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.debug_struct("LoadedBif")
152 .field("file_type", &self.file_type)
153 .field("file_version", &self.file_version)
154 .field("variable_resources", &self.variable_resources.len())
155 .field("oid", &self.oid)
156 .finish_non_exhaustive()
157 }
158}
159
160pub(crate) struct BifHandle {
161 pub filename: String,
162 pub resolver_filename: String,
163 pub expected_version: KeyBifVersion,
164 pub expected_oid: Option<String>,
165 pub drives: u16,
166 pub resolver: BifResolver,
167 pub loaded: Mutex<Option<Arc<LoadedBif>>>,
168}
169
170impl fmt::Debug for BifHandle {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 f.debug_struct("BifHandle")
173 .field("filename", &self.filename)
174 .field("expected_version", &self.expected_version)
175 .field("expected_oid", &self.expected_oid)
176 .finish_non_exhaustive()
177 }
178}
179
180#[derive(#[automatically_derived]
impl ::core::fmt::Debug for KeyEntry {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "KeyEntry",
"res_id", &self.res_id, "sha1", &&self.sha1)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for KeyEntry {
#[inline]
fn clone(&self) -> KeyEntry {
KeyEntry {
res_id: ::core::clone::Clone::clone(&self.res_id),
sha1: ::core::clone::Clone::clone(&self.sha1),
}
}
}Clone)]
181pub(crate) struct KeyEntry {
182 pub res_id: ResId,
183 pub sha1: SecureHash,
184}
185
186pub struct KeyTable {
198 pub(crate) version: KeyBifVersion,
199 pub(crate) label: String,
200 pub(crate) build_year: u32,
201 pub(crate) build_day: u32,
202 pub(crate) bifs: Vec<BifHandle>,
203 pub(crate) resref_id_lookup: IndexMap<ResRef, KeyEntry, RandomState>,
204 pub(crate) oid: Option<String>,
205 pub(crate) raw_oid: Option<String>,
206}
207
208#[derive(#[automatically_derived]
impl ::core::fmt::Debug for KeyBifContents {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"KeyBifContents", "filename", &self.filename, "resources",
&&self.resources)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for KeyBifContents {
#[inline]
fn clone(&self) -> KeyBifContents {
KeyBifContents {
filename: ::core::clone::Clone::clone(&self.filename),
resources: ::core::clone::Clone::clone(&self.resources),
}
}
}Clone)]
209pub struct KeyBifContents {
211 pub filename: String,
213 pub resources: Vec<ResRef>,
215}
216
217impl fmt::Debug for KeyTable {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.debug_struct("KeyTable")
220 .field("version", &self.version)
221 .field("label", &self.label)
222 .field("build_year", &self.build_year)
223 .field("build_day", &self.build_day)
224 .field("oid", &self.oid)
225 .field(
226 "bifs",
227 &self
228 .bifs
229 .iter()
230 .map(|bif| bif.filename.clone())
231 .collect::<Vec<_>>(),
232 )
233 .field("entry_count", &self.resref_id_lookup.len())
234 .finish_non_exhaustive()
235 }
236}
237
238impl fmt::Display for KeyTable {
239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 f.write_fmt(format_args!("KeyTable:{0}", self.label))write!(f, "KeyTable:{}", self.label)
241 }
242}
243
244impl ResContainer for KeyTable {
245 fn contains(&self, rr: &ResRef) -> bool {
246 self.resref_id_lookup.contains_key(rr)
247 }
248
249 fn demand(&self, rr: &ResRef) -> ResManResult<Res> {
250 let entry = self
251 .resref_id_lookup
252 .get(rr)
253 .ok_or_else(|| ResManError::Message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("not found: {0}", rr))
})format!("not found: {rr}")))?;
254 let bif_idx = (entry.res_id >> 20) as usize;
255 let variable_id = entry.res_id & 0x000f_ffff;
256 let bif = self.bifs.get(bif_idx).ok_or_else(|| {
257 ResManError::Message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("invalid bif index for {0}: {1}",
rr, bif_idx))
})format!("invalid bif index for {rr}: {bif_idx}"))
258 })?;
259 let loaded = bif
260 .load()
261 .map_err(|error| ResManError::Message(error.to_string()))?;
262 let variable = loaded.variable_resources.get(&variable_id).ok_or_else(|| {
263 ResManError::Message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("keytable references non-existent id: {0}",
entry.res_id))
})format!(
264 "keytable references non-existent id: {}",
265 entry.res_id
266 ))
267 })?;
268
269 Ok(Res::new_with_stream(
270 new_res_origin(
271 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("KeyTable:{0}", self.label))
})format!("KeyTable:{}", self.label),
272 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("id={0} in {1}", entry.res_id,
bif.filename))
})format!("id={} in {}", entry.res_id, bif.filename),
273 ),
274 rr.clone(),
275 SystemTime::UNIX_EPOCH,
276 loaded.stream.clone(),
277 i64::try_from(variable.io_size).map_err(|e| {
278 ResManError::Message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("KEY resource size exceeds i64 range: {0}",
e))
})format!("KEY resource size exceeds i64 range: {e}"))
279 })?,
280 variable.io_offset,
281 variable.compression_type,
282 None,
283 variable.uncompressed_size,
284 entry.sha1,
285 ))
286 }
287
288 fn count(&self) -> usize {
289 self.resref_id_lookup.len()
290 }
291
292 fn contents(&self) -> Vec<ResRef> {
293 self.resref_id_lookup.keys().cloned().collect()
294 }
295}
296
297impl KeyTable {
298 #[must_use]
300 pub fn version(&self) -> KeyBifVersion {
301 self.version
302 }
303
304 #[must_use]
306 pub fn build_year(&self) -> u32 {
307 self.build_year
308 }
309
310 #[must_use]
312 pub fn build_day(&self) -> u32 {
313 self.build_day
314 }
315
316 #[must_use]
318 pub fn oid(&self) -> Option<&str> {
319 self.oid.as_deref()
320 }
321
322 #[must_use]
324 pub fn raw_oid(&self) -> Option<&str> {
325 self.raw_oid.as_deref()
326 }
327
328 #[must_use]
330 pub fn bifs(&self) -> Vec<String> {
331 self.bifs.iter().map(|bif| bif.filename.clone()).collect()
332 }
333
334 pub fn bif_contents(&self) -> KeyResult<Vec<KeyBifContents>> {
343 let mut by_bif = Vec::with_capacity(self.bifs.len());
344
345 for (bif_idx, bif) in self.bifs.iter().enumerate() {
346 let loaded = bif.load()?;
347 let mut resources = Vec::with_capacity(loaded.variable_resources.len());
348
349 for local_id in loaded.variable_resources.keys() {
350 let full_id = (u32::try_from(bif_idx)
351 .map_err(|_error| KeyError::msg("bif index exceeds 32-bit range"))?
352 << 20)
353 | *local_id;
354 let rr = self
355 .resref_id_lookup
356 .iter()
357 .find_map(|(rr, entry)| (entry.res_id == full_id).then(|| rr.clone()))
358 .ok_or_else(|| {
359 KeyError::msg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("bif {0} references unknown variable resource id {1}",
bif.filename, full_id))
})format!(
360 "bif {} references unknown variable resource id {}",
361 bif.filename, full_id
362 ))
363 })?;
364 resources.push(rr);
365 }
366
367 by_bif.push(KeyBifContents {
368 filename: bif.filename.clone(),
369 resources,
370 });
371 }
372
373 Ok(by_bif)
374 }
375}
376
377impl BifHandle {
378 pub(crate) fn load(&self) -> KeyResult<Arc<LoadedBif>> {
379 {
380 let loaded = self
381 .loaded
382 .lock()
383 .map_err(|error| KeyError::msg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("bif lock poisoned: {0}", error))
})format!("bif lock poisoned: {error}")))?;
384 if let Some(loaded) = loaded.as_ref() {
385 return Ok(loaded.clone());
386 }
387 }
388
389 let stream = (self.resolver)(&self.resolver_filename)?.ok_or_else(|| {
390 KeyError::msg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("key file referenced file {0} but cannot open",
self.filename))
})format!(
391 "key file referenced file {} but cannot open",
392 self.filename
393 ))
394 })?;
395 let loaded = Arc::new(crate::io::read_bif(
396 stream.clone(),
397 &self.filename,
398 self.expected_version,
399 self.expected_oid.as_deref(),
400 )?);
401 *self
402 .loaded
403 .lock()
404 .map_err(|error| KeyError::msg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("bif lock poisoned: {0}", error))
})format!("bif lock poisoned: {error}")))? =
405 Some(loaded.clone());
406 Ok(loaded)
407 }
408}
409
410#[derive(#[automatically_derived]
impl ::core::fmt::Debug for KeyBifEntry {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["directory", "name", "recorded_filename", "drives", "bif_oid",
"entries"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.directory, &self.name, &self.recorded_filename,
&self.drives, &self.bif_oid, &&self.entries];
::core::fmt::Formatter::debug_struct_fields_finish(f, "KeyBifEntry",
names, values)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for KeyBifEntry {
#[inline]
fn clone(&self) -> KeyBifEntry {
KeyBifEntry {
directory: ::core::clone::Clone::clone(&self.directory),
name: ::core::clone::Clone::clone(&self.name),
recorded_filename: ::core::clone::Clone::clone(&self.recorded_filename),
drives: ::core::clone::Clone::clone(&self.drives),
bif_oid: ::core::clone::Clone::clone(&self.bif_oid),
entries: ::core::clone::Clone::clone(&self.entries),
}
}
}Clone)]
411pub struct KeyBifEntry {
413 pub directory: String,
415 pub name: String,
417 pub recorded_filename: Option<String>,
419 pub drives: u16,
421 pub bif_oid: Option<String>,
423 pub entries: Vec<ResRef>,
425}
426
427pub(crate) type WriteBifResult = (usize, Vec<(ResRef, SecureHash)>);