paramdex_rs/
lib.rs

1
2//! Utilities for handling and deserializing a Paramdex for modifying Souls games.
3//!
4//! Entry points for the library include:
5//! - [`Paramdex::deserialize_all`] - For deserializing an entire Paramdex
6//! - [`deserialize::deserialize_def`] - For deserializing a single Paramdef from a Paramdex
7//! - [`Paramdex::empty`] - For starting with an empty Paramdex to insert defs into.
8
9
10/// Utilities for deserializing [ParamDef]s from XML. Input should be from
11/// [soulsmods/Paramdex](https://github.com/soulsmods/Paramdex).
12pub mod deserialize;
13
14use std::collections::HashMap;
15use crate::deserialize::ParamdefDeserializeError;
16
17/// A simple mapping from param type to a [ParamDef]
18pub struct Paramdex {
19    /// internal backing map for [ParamDef]s
20    definitions: HashMap<String, ParamDef>,
21}
22
23impl Paramdex {
24    /// Insert a new [ParamDef] into the Paramdex
25    pub fn insert(&mut self, paramdef: ParamDef) -> Option<ParamDef> {
26        self.definitions.insert(paramdef.param_type.clone(), paramdef)
27    }
28
29    /// Retrieve a [ParamDef] based on a param type (encoded into params)
30    /// The relevant definition must first be inserted into the paramdex by using [`Paramdex::insert`]
31    /// or by deserializing the Paramdex.
32    ///
33    /// # See also
34    /// [`Paramdex::deserialize_all`]
35    pub fn get_param_def(&self, key: &str) -> Option<&ParamDef> {
36        self.definitions.get(key)
37    }
38
39    /// Deserialize a whole Paramdex from an iterator of &str
40    pub fn deserialize_all<I: IntoIterator<Item = S>, S: AsRef<str>>(input_iter: I) -> Result<Paramdex, ParamdefDeserializeError> {
41        let mut paramdex = Paramdex { definitions: HashMap::new() };
42
43        for input in input_iter {
44            let input = input.as_ref();
45            paramdex.insert(deserialize::deserialize_def(input)?);
46        }
47        Ok(paramdex)
48    }
49
50    /// Creates an empty Paramdex.
51    pub fn empty() -> Paramdex { Paramdex { definitions: HashMap::new() } }
52}
53
54/// The text format for descriptions in the [ParamDef]
55pub enum ParamdefFormat {
56    UTF16,
57    ShiftJIS,
58}
59
60/// The endianness of the specific [ParamDef]
61pub enum ParamdefEndian {
62    Little,
63    Big,
64}
65
66/// A definition for the format of a param file
67pub struct ParamDef {
68    /// The internal type key for the parameter
69    pub param_type: String,
70
71    /// The data version declared for the param
72    pub data_version: u32,
73
74    /// The endianness declared for the param
75    pub endian: ParamdefEndian,
76
77    /// The string encoding declared for the param
78    pub string_format: ParamdefFormat,
79
80    /// The version of the format for the XML
81    pub format_version: u32,
82
83    /// The fields present in the param. Ordered.
84    pub fields: Vec<ParamField>
85}
86
87/// The data type definition for a parameter field
88#[derive(Debug)]
89pub struct ParamFieldDef {
90    pub field_type: ParamFieldType,
91    pub name: String,
92    pub default_value: Option<f64>,
93}
94
95/// Declared metadata about fields in a param
96pub struct ParamField {
97    /// The definition of the field, including type and internal name, among others.
98    pub field_def: ParamFieldDef,
99
100    /// A user-friends display name.
101    pub display_name: Option<String>,
102
103    /// A type of enum declared by a paramdex that can be applied to this field. Unused.
104    pub enum_tdf: Option<String>,
105
106    /// A  user-friendly description
107    pub description: Option<String>,
108
109    /// A printf(3) compatible format string for printing the data in this field. Unused.
110    pub printf_format: Option<String>,
111
112    /// Flags that inform a potential editor how to handle this field. Unused.
113    pub edit_flags: Option<EditFlags>,
114
115    /// Minimum value allowed to be input in an editor. Unused.
116    pub minimum: Option<f64>,
117
118    /// Maximum value allowed to be input in an editor. Unused.
119    pub maximum: Option<f64>,
120
121    /// Increment value allowed to be input in an editor. Unused.
122    pub increment: Option<f64>,
123
124    /// Declares sorting for a potential editor. Unused.
125    pub sort_id: Option<usize>,
126}
127
128/// Flags used in editors to control user input behavior
129pub struct EditFlags {
130    pub wrap: bool,
131    pub lock: bool,
132}
133
134/// Type of field present in the param
135///
136/// \[su\]\(8\|16\|32\) are integer types, signed and unsigned respectively, with the
137/// appropriate bit sizes.
138#[allow(non_camel_case_types)]
139#[derive(PartialEq, Debug)]
140pub enum ParamFieldType {
141    /// Signed integer with size of 8 bits
142    s8,
143
144    /// Unsigned integer with size of 8 bits
145    u8 {
146        /// Optionally limited to number of bits to be read
147        bit_size: Option<u8>
148    },
149
150    /// Signed integer with size of 16 bits
151    s16,
152
153    /// Unsigned integer with size of 16 bits
154    u16 {
155        /// Optionally limited to number of bits to be read
156        bit_size: Option<u8>
157    },
158
159    /// Signed integer with size of 32 bits
160    s32,
161
162    /// Unsigned integer with size of 32 bits
163    u32 {
164        /// Optionally limited to number of bits to be read
165        bit_size: Option<u8>
166    },
167
168    /// Boolean value represented with 32 bits. 0 == `false`, !0 == `true`.
169    b32,
170
171    /// Single-precision floating point
172    f32,
173
174    /// Single-precision floating point, but this time references an angle. No real difference to [`ParamFieldType::f32`]
175    a32,
176
177    /// Double-precision floating point
178    f64,
179
180    /// Fixed-length string encoded in ShiftJIS.
181    fixstr {
182        /// Length of fixed-length string
183        length: usize,
184    },
185
186    /// Fixed-length string encoded in UTF16.
187    fixstrW {
188        /// Length of fixed-length string
189        length: usize,
190    },
191
192    /// Unused or unknown bytes or bits, likely used for padding
193    dummy8 {
194        /// Length of dummy data. 1 byte if `None`.
195        length: Option<DummyType>
196    },
197}
198
199/// Enum for type of dummy data
200#[derive(PartialEq, Debug)]
201pub enum DummyType {
202    /// Dummy data is in bytes, with a defined length
203    Bytes(usize),
204
205    ///  Dummy data is in bits, with a defined length
206    Bits(u8),
207}
208
209impl ParamFieldType {
210    /// Sets the bit size of a field type, on field types that support variable bit lengths.
211    ///
212    /// # Panics
213    /// Panics when the field type does not support bit size definitions. See [`ParamFieldType::supports_bit_size`]
214    pub fn set_bit_size(&mut self, new_bit_size: u8) {
215        match self {
216            Self::u8 {bit_size} | Self::u16 {bit_size} | Self::u32 {bit_size} => {
217                bit_size.replace(new_bit_size)
218            }
219            _ => panic!("Bit size not supported"),
220        };
221    }
222
223    /// Whether the given field type supports bit size definitions
224    pub fn supports_bit_size(&self) -> bool {
225        match self {
226            Self::u8 {..} | Self::u16 {..} | Self::u32 {..} => true,
227            _ => false,
228        }
229    }
230}