Skip to main content

nwnrs_ssf/
types.rs

1use std::{fmt, io};
2
3use nwnrs_localization::prelude::*;
4use serde::{Deserialize, Serialize};
5
6pub(crate) const HEADER_MAGIC: &str = "SSF ";
7pub(crate) const HEADER_VERSION: &str = "V1.0";
8pub(crate) const TABLE_OFFSET: u32 = 40;
9pub(crate) const ENTRY_DATA_SIZE: usize = 20;
10
11#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SsfError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            SsfError::Io(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Io",
                    &__self_0),
            SsfError::Message(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Message", &__self_0),
        }
    }
}Debug)]
12/// Errors returned while reading or writing SSF data.
13pub enum SsfError {
14    /// An underlying IO operation failed.
15    Io(io::Error),
16    /// The SSF contents were otherwise invalid.
17    Message(String),
18}
19
20impl fmt::Display for SsfError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Self::Io(error) => error.fmt(f),
24            Self::Message(message) => f.write_str(message),
25        }
26    }
27}
28
29impl std::error::Error for SsfError {}
30
31impl From<io::Error> for SsfError {
32    fn from(value: io::Error) -> Self {
33        Self::Io(value)
34    }
35}
36
37/// Result type for SSF operations.
38pub type SsfResult<T> = Result<T, SsfError>;
39
40/// A single soundset slot.
41#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SsfEntry {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "SsfEntry",
            "raw_resref", &self.raw_resref, "resref", &self.resref, "strref",
            &&self.strref)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for SsfEntry {
    #[inline]
    fn clone(&self) -> SsfEntry {
        SsfEntry {
            raw_resref: ::core::clone::Clone::clone(&self.raw_resref),
            resref: ::core::clone::Clone::clone(&self.resref),
            strref: ::core::clone::Clone::clone(&self.strref),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for SsfEntry {
    #[inline]
    fn eq(&self, other: &SsfEntry) -> bool {
        self.raw_resref == other.raw_resref && self.resref == other.resref &&
            self.strref == other.strref
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SsfEntry {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<[u8; 16]>;
        let _: ::core::cmp::AssertParamIsEq<String>;
        let _: ::core::cmp::AssertParamIsEq<StrRef>;
    }
}Eq, #[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl _serde::Serialize for SsfEntry {
            fn serialize<__S>(&self, __serializer: __S)
                -> _serde::__private228::Result<__S::Ok, __S::Error> where
                __S: _serde::Serializer {
                let mut __serde_state =
                    _serde::Serializer::serialize_struct(__serializer,
                            "SsfEntry", false as usize + 1 + 1 + 1)?;
                _serde::ser::SerializeStruct::serialize_field(&mut __serde_state,
                        "raw_resref", &self.raw_resref)?;
                _serde::ser::SerializeStruct::serialize_field(&mut __serde_state,
                        "resref", &self.resref)?;
                _serde::ser::SerializeStruct::serialize_field(&mut __serde_state,
                        "strref", &self.strref)?;
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
    };Serialize, #[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl<'de> _serde::Deserialize<'de> for SsfEntry {
            fn deserialize<__D>(__deserializer: __D)
                -> _serde::__private228::Result<Self, __D::Error> where
                __D: _serde::Deserializer<'de> {
                #[allow(non_camel_case_types)]
                #[doc(hidden)]
                enum __Field { __field0, __field1, __field2, __ignore, }
                #[doc(hidden)]
                struct __FieldVisitor;
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
                    type Value = __Field;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "field identifier")
                    }
                    fn visit_u64<__E>(self, __value: u64)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            0u64 => _serde::__private228::Ok(__Field::__field0),
                            1u64 => _serde::__private228::Ok(__Field::__field1),
                            2u64 => _serde::__private228::Ok(__Field::__field2),
                            _ => _serde::__private228::Ok(__Field::__ignore),
                        }
                    }
                    fn visit_str<__E>(self, __value: &str)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            "raw_resref" => _serde::__private228::Ok(__Field::__field0),
                            "resref" => _serde::__private228::Ok(__Field::__field1),
                            "strref" => _serde::__private228::Ok(__Field::__field2),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                    fn visit_bytes<__E>(self, __value: &[u8])
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            b"raw_resref" =>
                                _serde::__private228::Ok(__Field::__field0),
                            b"resref" => _serde::__private228::Ok(__Field::__field1),
                            b"strref" => _serde::__private228::Ok(__Field::__field2),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                }
                #[automatically_derived]
                impl<'de> _serde::Deserialize<'de> for __Field {
                    #[inline]
                    fn deserialize<__D>(__deserializer: __D)
                        -> _serde::__private228::Result<Self, __D::Error> where
                        __D: _serde::Deserializer<'de> {
                        _serde::Deserializer::deserialize_identifier(__deserializer,
                            __FieldVisitor)
                    }
                }
                #[doc(hidden)]
                struct __Visitor<'de> {
                    marker: _serde::__private228::PhantomData<SsfEntry>,
                    lifetime: _serde::__private228::PhantomData<&'de ()>,
                }
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
                    type Value = SsfEntry;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "struct SsfEntry")
                    }
                    #[inline]
                    fn visit_seq<__A>(self, mut __seq: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::SeqAccess<'de> {
                        let __field0 =
                            match _serde::de::SeqAccess::next_element::<[u8; 16]>(&mut __seq)?
                                {
                                _serde::__private228::Some(__value) => __value,
                                _serde::__private228::None =>
                                    return _serde::__private228::Err(_serde::de::Error::invalid_length(0usize,
                                                &"struct SsfEntry with 3 elements")),
                            };
                        let __field1 =
                            match _serde::de::SeqAccess::next_element::<String>(&mut __seq)?
                                {
                                _serde::__private228::Some(__value) => __value,
                                _serde::__private228::None =>
                                    return _serde::__private228::Err(_serde::de::Error::invalid_length(1usize,
                                                &"struct SsfEntry with 3 elements")),
                            };
                        let __field2 =
                            match _serde::de::SeqAccess::next_element::<StrRef>(&mut __seq)?
                                {
                                _serde::__private228::Some(__value) => __value,
                                _serde::__private228::None =>
                                    return _serde::__private228::Err(_serde::de::Error::invalid_length(2usize,
                                                &"struct SsfEntry with 3 elements")),
                            };
                        _serde::__private228::Ok(SsfEntry {
                                raw_resref: __field0,
                                resref: __field1,
                                strref: __field2,
                            })
                    }
                    #[inline]
                    fn visit_map<__A>(self, mut __map: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::MapAccess<'de> {
                        let mut __field0: _serde::__private228::Option<[u8; 16]> =
                            _serde::__private228::None;
                        let mut __field1: _serde::__private228::Option<String> =
                            _serde::__private228::None;
                        let mut __field2: _serde::__private228::Option<StrRef> =
                            _serde::__private228::None;
                        while let _serde::__private228::Some(__key) =
                                _serde::de::MapAccess::next_key::<__Field>(&mut __map)? {
                            match __key {
                                __Field::__field0 => {
                                    if _serde::__private228::Option::is_some(&__field0) {
                                        return _serde::__private228::Err(<__A::Error as
                                                        _serde::de::Error>::duplicate_field("raw_resref"));
                                    }
                                    __field0 =
                                        _serde::__private228::Some(_serde::de::MapAccess::next_value::<[u8; 16]>(&mut __map)?);
                                }
                                __Field::__field1 => {
                                    if _serde::__private228::Option::is_some(&__field1) {
                                        return _serde::__private228::Err(<__A::Error as
                                                        _serde::de::Error>::duplicate_field("resref"));
                                    }
                                    __field1 =
                                        _serde::__private228::Some(_serde::de::MapAccess::next_value::<String>(&mut __map)?);
                                }
                                __Field::__field2 => {
                                    if _serde::__private228::Option::is_some(&__field2) {
                                        return _serde::__private228::Err(<__A::Error as
                                                        _serde::de::Error>::duplicate_field("strref"));
                                    }
                                    __field2 =
                                        _serde::__private228::Some(_serde::de::MapAccess::next_value::<StrRef>(&mut __map)?);
                                }
                                _ => {
                                    let _ =
                                        _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?;
                                }
                            }
                        }
                        let __field0 =
                            match __field0 {
                                _serde::__private228::Some(__field0) => __field0,
                                _serde::__private228::None =>
                                    _serde::__private228::de::missing_field("raw_resref")?,
                            };
                        let __field1 =
                            match __field1 {
                                _serde::__private228::Some(__field1) => __field1,
                                _serde::__private228::None =>
                                    _serde::__private228::de::missing_field("resref")?,
                            };
                        let __field2 =
                            match __field2 {
                                _serde::__private228::Some(__field2) => __field2,
                                _serde::__private228::None =>
                                    _serde::__private228::de::missing_field("strref")?,
                            };
                        _serde::__private228::Ok(SsfEntry {
                                raw_resref: __field0,
                                resref: __field1,
                                strref: __field2,
                            })
                    }
                }
                #[doc(hidden)]
                const FIELDS: &'static [&'static str] =
                    &["raw_resref", "resref", "strref"];
                _serde::Deserializer::deserialize_struct(__deserializer,
                    "SsfEntry", FIELDS,
                    __Visitor {
                        marker: _serde::__private228::PhantomData::<SsfEntry>,
                        lifetime: _serde::__private228::PhantomData,
                    })
            }
        }
    };Deserialize)]
42pub struct SsfEntry {
43    /// The raw 16-byte resref slot as stored on disk.
44    pub raw_resref: [u8; 16],
45    /// The sound resource reference stored for the slot.
46    pub resref:     String,
47    /// The localized string reference associated with the slot.
48    pub strref:     StrRef,
49}
50
51impl SsfEntry {
52    /// Creates a canonical SSF slot with a zero-padded resref encoding.
53    pub fn new(resref: impl Into<String>, strref: StrRef) -> Self {
54        let resref = resref.into();
55        let mut raw_resref = [0_u8; 16];
56        let bytes = resref.as_bytes();
57        let count = bytes.len().min(raw_resref.len());
58        if let (Some(dst), Some(src)) = (raw_resref.get_mut(..count), bytes.get(..count)) {
59            dst.copy_from_slice(src);
60        }
61        Self {
62            raw_resref,
63            resref,
64            strref,
65        }
66    }
67
68    pub(crate) fn stored_resref_bytes(&self) -> io::Result<[u8; 16]> {
69        if decode_resref(&self.raw_resref) == self.resref {
70            return Ok(self.raw_resref);
71        }
72
73        if self.resref.len() > 16 {
74            return Err(io::Error::new(
75                io::ErrorKind::InvalidData,
76                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("resref {0:?} exceeds 16 bytes",
                self.resref))
    })format!("resref {:?} exceeds 16 bytes", self.resref),
77            ));
78        }
79
80        let mut padded = [0_u8; 16];
81        let bytes = self.resref.as_bytes();
82        if let Some(prefix) = padded.get_mut(..bytes.len()) {
83            prefix.copy_from_slice(bytes);
84        }
85        Ok(padded)
86    }
87}
88
89/// The decoded contents of an `SSF` file.
90#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SsfRoot {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "SsfRoot",
            "entries", &&self.entries)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for SsfRoot {
    #[inline]
    fn clone(&self) -> SsfRoot {
        SsfRoot { entries: ::core::clone::Clone::clone(&self.entries) }
    }
}Clone, #[automatically_derived]
impl ::core::default::Default for SsfRoot {
    #[inline]
    fn default() -> SsfRoot {
        SsfRoot { entries: ::core::default::Default::default() }
    }
}Default, #[automatically_derived]
impl ::core::cmp::PartialEq for SsfRoot {
    #[inline]
    fn eq(&self, other: &SsfRoot) -> bool { self.entries == other.entries }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SsfRoot {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Vec<SsfEntry>>;
    }
}Eq, #[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl _serde::Serialize for SsfRoot {
            fn serialize<__S>(&self, __serializer: __S)
                -> _serde::__private228::Result<__S::Ok, __S::Error> where
                __S: _serde::Serializer {
                let mut __serde_state =
                    _serde::Serializer::serialize_struct(__serializer,
                            "SsfRoot", false as usize + 1)?;
                _serde::ser::SerializeStruct::serialize_field(&mut __serde_state,
                        "entries", &self.entries)?;
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
    };Serialize, #[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications,
clippy :: absolute_paths,)]
const _: () =
    {
        #[allow(unused_extern_crates, clippy :: useless_attribute)]
        extern crate serde as _serde;
        ;
        #[automatically_derived]
        impl<'de> _serde::Deserialize<'de> for SsfRoot {
            fn deserialize<__D>(__deserializer: __D)
                -> _serde::__private228::Result<Self, __D::Error> where
                __D: _serde::Deserializer<'de> {
                #[allow(non_camel_case_types)]
                #[doc(hidden)]
                enum __Field { __field0, __ignore, }
                #[doc(hidden)]
                struct __FieldVisitor;
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
                    type Value = __Field;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "field identifier")
                    }
                    fn visit_u64<__E>(self, __value: u64)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            0u64 => _serde::__private228::Ok(__Field::__field0),
                            _ => _serde::__private228::Ok(__Field::__ignore),
                        }
                    }
                    fn visit_str<__E>(self, __value: &str)
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            "entries" => _serde::__private228::Ok(__Field::__field0),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                    fn visit_bytes<__E>(self, __value: &[u8])
                        -> _serde::__private228::Result<Self::Value, __E> where
                        __E: _serde::de::Error {
                        match __value {
                            b"entries" => _serde::__private228::Ok(__Field::__field0),
                            _ => { _serde::__private228::Ok(__Field::__ignore) }
                        }
                    }
                }
                #[automatically_derived]
                impl<'de> _serde::Deserialize<'de> for __Field {
                    #[inline]
                    fn deserialize<__D>(__deserializer: __D)
                        -> _serde::__private228::Result<Self, __D::Error> where
                        __D: _serde::Deserializer<'de> {
                        _serde::Deserializer::deserialize_identifier(__deserializer,
                            __FieldVisitor)
                    }
                }
                #[doc(hidden)]
                struct __Visitor<'de> {
                    marker: _serde::__private228::PhantomData<SsfRoot>,
                    lifetime: _serde::__private228::PhantomData<&'de ()>,
                }
                #[automatically_derived]
                impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
                    type Value = SsfRoot;
                    fn expecting(&self,
                        __formatter: &mut _serde::__private228::Formatter)
                        -> _serde::__private228::fmt::Result {
                        _serde::__private228::Formatter::write_str(__formatter,
                            "struct SsfRoot")
                    }
                    #[inline]
                    fn visit_seq<__A>(self, mut __seq: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::SeqAccess<'de> {
                        let __field0 =
                            match _serde::de::SeqAccess::next_element::<Vec<SsfEntry>>(&mut __seq)?
                                {
                                _serde::__private228::Some(__value) => __value,
                                _serde::__private228::None =>
                                    return _serde::__private228::Err(_serde::de::Error::invalid_length(0usize,
                                                &"struct SsfRoot with 1 element")),
                            };
                        _serde::__private228::Ok(SsfRoot { entries: __field0 })
                    }
                    #[inline]
                    fn visit_map<__A>(self, mut __map: __A)
                        -> _serde::__private228::Result<Self::Value, __A::Error>
                        where __A: _serde::de::MapAccess<'de> {
                        let mut __field0:
                                _serde::__private228::Option<Vec<SsfEntry>> =
                            _serde::__private228::None;
                        while let _serde::__private228::Some(__key) =
                                _serde::de::MapAccess::next_key::<__Field>(&mut __map)? {
                            match __key {
                                __Field::__field0 => {
                                    if _serde::__private228::Option::is_some(&__field0) {
                                        return _serde::__private228::Err(<__A::Error as
                                                        _serde::de::Error>::duplicate_field("entries"));
                                    }
                                    __field0 =
                                        _serde::__private228::Some(_serde::de::MapAccess::next_value::<Vec<SsfEntry>>(&mut __map)?);
                                }
                                _ => {
                                    let _ =
                                        _serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)?;
                                }
                            }
                        }
                        let __field0 =
                            match __field0 {
                                _serde::__private228::Some(__field0) => __field0,
                                _serde::__private228::None =>
                                    _serde::__private228::de::missing_field("entries")?,
                            };
                        _serde::__private228::Ok(SsfRoot { entries: __field0 })
                    }
                }
                #[doc(hidden)]
                const FIELDS: &'static [&'static str] = &["entries"];
                _serde::Deserializer::deserialize_struct(__deserializer,
                    "SsfRoot", FIELDS,
                    __Visitor {
                        marker: _serde::__private228::PhantomData::<SsfRoot>,
                        lifetime: _serde::__private228::PhantomData,
                    })
            }
        }
    };Deserialize)]
91pub struct SsfRoot {
92    /// The ordered soundset entries in the file.
93    pub entries: Vec<SsfEntry>,
94}
95
96impl SsfRoot {
97    /// Creates an empty `SSF` document.
98    #[must_use]
99    pub fn new() -> Self {
100        Self::default()
101    }
102}
103
104pub(crate) fn decode_resref(raw: &[u8]) -> String {
105    let end = raw.iter().position(|byte| *byte == 0).unwrap_or(raw.len());
106    String::from_utf8_lossy(raw.get(..end).unwrap_or(&[])).to_string()
107}