Skip to main content

cdragon_prop/
data.rs

1//! Bin data definitions
2use std::any::Any;
3use num_enum::TryFromPrimitive;
4use super::BinHashMappers;
5use cdragon_hashes::{
6    define_hash_type,
7    HashOrStr,
8    bin::{BinHashKind, compute_binhash},
9    wad::compute_wad_hash,
10};
11pub use cdragon_hashes::bin::BinHashMapper;
12
13
14/// Field value for an antry, a struct or an embed
15#[derive(Debug)]
16pub struct BinField {
17    /// Field name (hashed)
18    pub name: BinFieldName,
19    /// Field value type
20    pub vtype: BinType,
21    pub(crate) value: Box<dyn Any>,  // Any = vtype
22}
23
24impl BinField {
25    /// Downcast the field value
26    pub fn downcast<T: BinValue + 'static>(&self) -> Option<&T> {
27        self.value.downcast_ref::<T>()
28    }
29}
30
31
32/// Declare a bin hash type
33macro_rules! declare_bin_hash {
34    (
35        $(#[$meta:meta])*
36        $name:ident => $kind:expr
37    ) => {
38        define_hash_type! {
39            $(#[$meta])*
40            $name(u32) => compute_binhash
41        }
42
43        impl $name {
44            /// Hash kind, for use with [BinHashMappers]
45            const KIND: BinHashKind = $kind;
46            /// Get the string associated to the hash
47            pub fn get_str<'a>(&self, mapper: &'a BinHashMappers) -> Option<&'a str> {
48                mapper.get(Self::KIND).get(self.hash)
49            }
50            /// Get the string associated to the hash or fallback to the hash itself
51            pub fn seek_str<'a>(&self, mapper: &'a BinHashMappers) -> HashOrStr<u32, &'a str> {
52                mapper.get(Self::KIND).seek(self.hash)
53            }
54        }
55    }
56}
57
58declare_bin_hash! {
59    /// Hash of a [BinEntry] path
60    BinEntryPath => BinHashKind::EntryPath
61}
62declare_bin_hash! {
63    /// Hash of a bin class name (type of [entries](BinEntry), [structs](BinStruct) and
64    /// [embeds](BinEmbed))
65    BinClassName => BinHashKind::ClassName
66}
67declare_bin_hash! {
68    /// Hash of a field name of bin class
69    BinFieldName => BinHashKind::FieldName
70}
71declare_bin_hash! {
72    /// Hash of a [BinHash] value
73    BinHashValue => BinHashKind::HashValue
74}
75
76define_hash_type! {
77    /// Hash of a [BinPath] value, put to a file in a [cdragon_wad::Wad] archive
78    BinPathValue(u64) => compute_wad_hash
79}
80impl BinPathValue {
81    /// Get the path associated to the hash
82    pub fn get_str<'a>(&self, mapper: &'a BinHashMappers) -> Option<&'a str> {
83        mapper.path_value.get(self.hash)
84    }
85    /// Get the path associated to the hash or fallback to the hash itself
86    pub fn seek_str<'a>(&self, mapper: &'a BinHashMappers) -> HashOrStr<u64, &'a str> {
87        mapper.path_value.seek(self.hash)
88    }
89}
90
91
92/// Trait for values enumerated in [BinType]
93pub trait BinValue {
94    /// Bin type associated to the value
95    const TYPE: BinType;
96}
97
98macro_rules! declare_bintype_struct {
99    ($type:ident ($t:ty) [$($d:ident),* $(,)?]) => {
100        #[allow(missing_docs)]
101        #[derive(Debug,$($d),*)]
102        pub struct $type(pub $t);
103        impl From<$t> for $type {
104            fn from(v: $t) -> Self { Self(v) }
105        }
106    };
107    ($type:ident ($($v:ident: $t:ty),* $(,)?)) => {
108        #[allow(missing_docs)]
109        #[derive(Debug)]
110        pub struct $type($(pub $t,)*);
111        impl From<($($t),*)> for $type {
112            fn from(($($v),*): ($($t),*)) -> Self {
113                Self($($v),*)
114            }
115        }
116    };
117}
118
119declare_bintype_struct!{ BinNone() }
120declare_bintype_struct!{ BinBool(bool) [Eq,PartialEq,Hash] }
121declare_bintype_struct!{ BinS8(i8) [Eq,PartialEq,Hash] }
122declare_bintype_struct!{ BinU8(u8) [Eq,PartialEq,Hash] }
123declare_bintype_struct!{ BinS16(i16) [Eq,PartialEq,Hash] }
124declare_bintype_struct!{ BinU16(u16) [Eq,PartialEq,Hash] }
125declare_bintype_struct!{ BinS32(i32) [Eq,PartialEq,Hash] }
126declare_bintype_struct!{ BinU32(u32) [Eq,PartialEq,Hash] }
127declare_bintype_struct!{ BinS64(i64) [Eq,PartialEq,Hash] }
128declare_bintype_struct!{ BinU64(u64) [Eq,PartialEq,Hash] }
129declare_bintype_struct!{ BinFloat(f32) [] }
130declare_bintype_struct!{ BinVec2(a: f32, b: f32) }
131declare_bintype_struct!{ BinVec3(a: f32, b: f32, c: f32) }
132declare_bintype_struct!{ BinVec4(a: f32, b: f32, c: f32, d: f32) }
133declare_bintype_struct!{ BinMatrix([[f32; 4]; 4]) [] }
134/// Color bin value (RGBA)
135#[allow(missing_docs)]
136#[derive(Debug)]
137pub struct BinColor { pub r: u8, pub g: u8, pub b: u8, pub a: u8 }
138declare_bintype_struct!{ BinString(String) [Eq,PartialEq,Hash] }
139declare_bintype_struct!{ BinHash(BinHashValue) [Eq,PartialEq,Hash] }
140declare_bintype_struct!{ BinPath(BinPathValue) [Eq,PartialEq,Hash] }
141declare_bintype_struct!{ BinLink(BinEntryPath) [Eq,PartialEq,Hash] }
142declare_bintype_struct!{ BinFlag(bool) [Eq,PartialEq,Hash] }
143
144
145/// List of values, variable size
146///
147/// This type is used for both [BinType::List] and [BinType::List2].
148pub struct BinList {
149    /// Type of values in the list
150    pub vtype: BinType,
151    pub(crate) values: Box<dyn Any>,  // Any = Vec<vtype>
152}
153
154impl BinList {
155    /// Downcast the list to a vector
156    pub fn downcast<T: BinValue + 'static>(&self) -> Option<&Vec<T>> {
157        self.values.downcast_ref::<Vec<T>>()
158    }
159}
160
161/// Bin structure, referenced by pointer
162pub struct BinStruct {
163    /// Class type of the struct
164    pub ctype: BinClassName,
165    /// Struct fields
166    pub fields: Vec<BinField>,
167}
168
169impl BinStruct {
170    /// Get a field by its name
171    pub fn get(&self, name: BinFieldName) -> Option<&BinField> {
172        self.fields.iter().find(|f| f.name == name)
173    }
174
175    /// Get a field by its name and downcast it
176    pub fn getv<T: BinValue + 'static>(&self, name: BinFieldName) -> Option<&T> {
177        self.get(name).and_then(|field| field.downcast::<T>())
178    }
179}
180
181/// Bin structure whose data is embedded directly
182pub struct BinEmbed {
183    /// Class type of the embed
184    pub ctype: BinClassName,
185    /// Embed fields
186    pub fields: Vec<BinField>,
187}
188
189impl BinEmbed {
190    /// Get a field by its name
191    pub fn get(&self, name: BinFieldName) -> Option<&BinField> {
192        self.fields.iter().find(|f| f.name == name)
193    }
194
195    /// Get a field by its name and downcast it
196    pub fn getv<T: BinValue + 'static>(&self, name: BinFieldName) -> Option<&T> {
197        self.get(name).and_then(|field| field.downcast::<T>())
198    }
199}
200
201/// Optional bin value
202pub struct BinOption {
203    /// Type of the value in the option
204    pub vtype: BinType,
205    pub(crate) value: Option<Box<dyn Any>>,  // Any = vtype
206}
207
208impl BinOption {
209    /// Return `true` if the option contains a value
210    pub fn is_some(&self) -> bool {
211        self.value.is_some()
212    }
213
214    /// Downcast the option
215    pub fn downcast<T: BinValue + 'static>(&self) -> Option<&T> {
216        match self.value {
217            Some(ref v) => Some(v.downcast_ref::<T>()?),
218            None => None,
219        }
220    }
221}
222
223
224/// Map of values, with separate key and value types
225pub struct BinMap {
226    /// Type of map keys
227    pub ktype: BinType,
228    /// Type of map values
229    pub vtype: BinType,
230    pub(crate) values: Box<dyn Any>,  // Any = Vec<(ktype, vtype)>
231}
232
233impl BinMap {
234    /// Downcast the map to a vector of `(key, value)` pairs
235    pub fn downcast<K: BinValue + 'static, V: BinValue + 'static>(&self) -> Option<&Vec<(K, V)>> {
236        self.values.downcast_ref::<Vec<(K, V)>>()
237    }
238}
239
240impl BinValue for BinNone { const TYPE: BinType = BinType::None; }
241impl BinValue for BinBool { const TYPE: BinType = BinType::Bool; }
242impl BinValue for BinS8 { const TYPE: BinType = BinType::S8; }
243impl BinValue for BinU8 { const TYPE: BinType = BinType::U8; }
244impl BinValue for BinS16 { const TYPE: BinType = BinType::S16; }
245impl BinValue for BinU16 { const TYPE: BinType = BinType::U16; }
246impl BinValue for BinS32 { const TYPE: BinType = BinType::S32; }
247impl BinValue for BinU32 { const TYPE: BinType = BinType::U32; }
248impl BinValue for BinS64 { const TYPE: BinType = BinType::S64; }
249impl BinValue for BinU64 { const TYPE: BinType = BinType::U64; }
250impl BinValue for BinFloat { const TYPE: BinType = BinType::Float; }
251impl BinValue for BinVec2 { const TYPE: BinType = BinType::Vec2; }
252impl BinValue for BinVec3 { const TYPE: BinType = BinType::Vec3; }
253impl BinValue for BinVec4 { const TYPE: BinType = BinType::Vec4; }
254impl BinValue for BinMatrix { const TYPE: BinType = BinType::Matrix; }
255impl BinValue for BinColor { const TYPE: BinType = BinType::Color; }
256impl BinValue for BinString { const TYPE: BinType = BinType::String; }
257impl BinValue for BinHash { const TYPE: BinType = BinType::Hash; }
258impl BinValue for BinPath { const TYPE: BinType = BinType::Path; }
259impl BinValue for BinList { const TYPE: BinType = BinType::List; }
260impl BinValue for BinStruct { const TYPE: BinType = BinType::Struct; }
261impl BinValue for BinEmbed { const TYPE: BinType = BinType::Embed; }
262impl BinValue for BinLink { const TYPE: BinType = BinType::Link; }
263impl BinValue for BinOption { const TYPE: BinType = BinType::Option; }
264impl BinValue for BinMap { const TYPE: BinType = BinType::Map; }
265impl BinValue for BinFlag { const TYPE: BinType = BinType::Flag; }
266
267
268/// Basic bin types
269///
270/// Variant values match the binary values used in PROP files.
271#[allow(dead_code, missing_docs)]
272#[repr(u8)]
273#[derive(Copy, Clone, Eq, PartialEq, TryFromPrimitive, Debug)]
274pub enum BinType {
275    None = 0,
276    Bool = 1,
277    S8 = 2,
278    U8 = 3,
279    S16 = 4,
280    U16 = 5,
281    S32 = 6,
282    U32 = 7,
283    S64 = 8,
284    U64 = 9,
285    Float = 10,
286    Vec2 = 11,
287    Vec3 = 12,
288    Vec4 = 13,
289    Matrix = 14,
290    Color = 15,
291    String = 16,
292    Hash = 17,
293    Path = 18,  // introduced in 10.23
294    // Complex types (shifted to 0x80+ in 9.23)
295    List = 19,
296    List2 = 20,  // handled as List, introduced in 10.8
297    Struct = 21,
298    Embed = 22,
299    Link = 23,
300    Option = 24,
301    Map = 25,
302    Flag = 26,
303}
304
305impl BinType {
306    /// Return true for nested types
307    #[inline]
308    pub const fn is_nested(&self) -> bool {
309        matches!(self,
310            BinType::List |
311            BinType::List2 |
312            BinType::Struct |
313            BinType::Embed |
314            BinType::Option |
315            BinType::Map)
316    }
317}
318