1use crate::ByteBuffer;
5use crate::dat::DatHeader;
6use crate::equipment::Slot;
7use binrw::NullString;
8use binrw::binrw;
9use binrw::{BinRead, BinWrite};
10use std::collections::HashMap;
11use std::io::BufWriter;
12use std::io::Cursor;
13use std::io::Read;
14
15const UNKNOWN_FLAG: u32 = 1_000_000;
17
18fn convert_from_gear_id(id: u32) -> u32 {
19 id & !UNKNOWN_FLAG
20}
21
22fn convert_to_gear_id(id: &u32) -> u32 {
23 id | UNKNOWN_FLAG
24}
25
26fn convert_to_string(s: NullString) -> String {
27 s.to_string()
28}
29
30fn convert_from_string(s: &String) -> NullString {
31 NullString::from(s.as_str())
32}
33
34const NUMBER_OF_GEARSETS: usize = 100;
35
36fn convert_from_gearsets(gearsets: [GearSet; NUMBER_OF_GEARSETS]) -> Vec<Option<GearSet>> {
37 gearsets
38 .iter()
39 .cloned()
40 .map(|x| if !x.name.is_empty() { Some(x) } else { None })
41 .collect()
42}
43
44fn convert_to_gearsets(gearsets: &Vec<Option<GearSet>>) -> Vec<GearSet> {
45 let mut result = vec![GearSet::default(); NUMBER_OF_GEARSETS];
46 for (i, gearset) in gearsets.iter().enumerate() {
47 if i >= NUMBER_OF_GEARSETS {
48 break;
49 }
50 if let Some(gearset) = gearset {
51 result[i] = gearset.clone();
52 }
53 }
54 result
55}
56
57const NUMBER_OF_GEARSLOTS: usize = 14;
58
59fn convert_from_slots(slots: [GearSlot; NUMBER_OF_GEARSLOTS]) -> HashMap<GearSlotType, GearSlot> {
60 slots
61 .iter()
62 .cloned()
63 .enumerate()
64 .filter_map(|(i, x)| match x.id {
65 0 => None,
66 _ => Some((i.try_into().ok()?, x)),
67 })
68 .collect()
69}
70
71fn convert_to_slots(slots: &HashMap<GearSlotType, GearSlot>) -> Vec<GearSlot> {
72 let mut result = vec![GearSlot::default(); NUMBER_OF_GEARSLOTS];
73 for (idx, slot) in slots.iter() {
74 result[idx.clone() as usize] = slot.clone();
75 }
76 result
77}
78
79fn convert_id_opt(id: u32) -> Option<u32> {
80 if id == 0 { None } else { Some(id) }
81}
82
83fn convert_opt_id(id: &Option<u32>) -> u32 {
84 id.unwrap_or(0)
85}
86
87#[binrw]
88#[derive(Debug, Clone, Default)]
89pub struct GearSlot {
90 #[br(map = convert_from_gear_id)]
91 #[bw(map = convert_to_gear_id)]
92 pub id: u32,
94 #[br(map = convert_id_opt)]
95 #[bw(map = convert_opt_id)]
96 pub glamour_id: Option<u32>,
98 unknown1: u32,
100 unknown2: u32,
101 unknown3: u32,
102 unknown4: u32,
103 unknown5: u32,
104}
105
106#[derive(Debug, Clone, Hash, Eq, PartialEq)]
107pub enum GearSlotType {
108 MainHand = 0,
109 SecondaryHand,
110 Head,
111 Body,
112 Hands,
113 Waist, Legs,
115 Feet,
116 Bracelets,
117 Necklace,
118 Earrings,
119 Ring1,
120 Ring2,
121 Soul,
122}
123
124impl GearSlotType {
125 pub fn to_slot(&self) -> Option<Slot> {
126 match self {
127 GearSlotType::Head => Some(Slot::Head),
128 GearSlotType::Body => Some(Slot::Body),
129 GearSlotType::Hands => Some(Slot::Hands),
130 GearSlotType::Legs => Some(Slot::Legs),
131 GearSlotType::Feet => Some(Slot::Feet),
132 GearSlotType::Bracelets => Some(Slot::Wrists),
133 GearSlotType::Necklace => Some(Slot::Neck),
134 GearSlotType::Earrings => Some(Slot::Earring),
135 GearSlotType::Ring1 => Some(Slot::RingLeft),
136 GearSlotType::Ring2 => Some(Slot::RingRight),
137 _ => None,
138 }
139 }
140}
141
142impl TryFrom<Slot> for GearSlotType {
143 type Error = ();
144
145 fn try_from(v: Slot) -> Result<Self, Self::Error> {
146 match v {
147 Slot::Head => Ok(GearSlotType::Head),
148 Slot::Body => Ok(GearSlotType::Body),
149 Slot::Hands => Ok(GearSlotType::Hands),
150 Slot::Legs => Ok(GearSlotType::Legs),
151 Slot::Feet => Ok(GearSlotType::Feet),
152 Slot::Wrists => Ok(GearSlotType::Bracelets),
153 Slot::Neck => Ok(GearSlotType::Necklace),
154 Slot::Earring => Ok(GearSlotType::Earrings),
155 Slot::RingLeft => Ok(GearSlotType::Ring1),
156 Slot::RingRight => Ok(GearSlotType::Ring2),
157 }
158 }
159}
160
161impl TryFrom<usize> for GearSlotType {
162 type Error = ();
163
164 fn try_from(v: usize) -> Result<Self, Self::Error> {
165 match v {
166 x if x == GearSlotType::MainHand as usize => Ok(GearSlotType::MainHand),
167 x if x == GearSlotType::SecondaryHand as usize => Ok(GearSlotType::SecondaryHand),
168 x if x == GearSlotType::Head as usize => Ok(GearSlotType::Head),
169 x if x == GearSlotType::Body as usize => Ok(GearSlotType::Body),
170 x if x == GearSlotType::Hands as usize => Ok(GearSlotType::Hands),
171 x if x == GearSlotType::Waist as usize => Ok(GearSlotType::Waist),
172 x if x == GearSlotType::Legs as usize => Ok(GearSlotType::Legs),
173 x if x == GearSlotType::Feet as usize => Ok(GearSlotType::Feet),
174 x if x == GearSlotType::Bracelets as usize => Ok(GearSlotType::Bracelets),
175 x if x == GearSlotType::Necklace as usize => Ok(GearSlotType::Necklace),
176 x if x == GearSlotType::Earrings as usize => Ok(GearSlotType::Earrings),
177 x if x == GearSlotType::Ring1 as usize => Ok(GearSlotType::Ring1),
178 x if x == GearSlotType::Ring2 as usize => Ok(GearSlotType::Ring2),
179 x if x == GearSlotType::Soul as usize => Ok(GearSlotType::Soul),
180 _ => Err(()),
181 }
182 }
183}
184
185#[binrw]
186#[derive(Debug, Clone, Default)]
187pub struct GearSet {
188 pub index: u8,
190 #[brw(pad_size_to = 47)]
191 #[br(map = convert_to_string)]
192 #[bw(map = convert_from_string)]
193 pub name: String,
195 unknown1: u64,
197 #[br(map = convert_from_slots)]
198 #[bw(map = convert_to_slots)]
199 pub slots: HashMap<GearSlotType, GearSlot>,
201 #[br(map = convert_id_opt)]
202 #[bw(map = convert_opt_id)]
203 pub facewear: Option<u32>,
205}
206
207#[binrw]
208#[br(little)]
209#[derive(Debug, Clone)]
210pub struct GearSets {
211 unknown1: u8,
213 pub current_gearset: u8,
215 unknown3: u16,
217 #[br(map = convert_from_gearsets)]
218 #[bw(map = convert_to_gearsets)]
219 pub gearsets: Vec<Option<GearSet>>,
221}
222
223const GEARSET_KEY: u8 = 0x73;
224
225impl GearSets {
226 pub fn from_existing(buffer: &[u8]) -> Option<GearSets> {
228 let mut cursor = Cursor::new(buffer);
229
230 let header = DatHeader::read(&mut cursor).ok()?;
231
232 let mut buffer = vec![0; header.content_size as usize - 1];
233 cursor.read_exact(&mut buffer).ok()?;
234
235 let decoded = buffer.iter().map(|x| *x ^ GEARSET_KEY).collect::<Vec<_>>();
236 let mut cursor = Cursor::new(decoded);
237
238 GearSets::read(&mut cursor).ok()
239 }
240
241 pub fn write_to_buffer(&self) -> Option<ByteBuffer> {
243 let mut buffer = ByteBuffer::new();
244
245 {
247 let mut cursor = Cursor::new(&mut buffer);
248
249 let header = DatHeader {
250 file_type: crate::dat::DatFileType::Gearset,
251 max_size: 45205,
252 content_size: 45205,
253 };
254 header.write_le(&mut cursor).ok()?
255 }
256
257 {
259 let mut cursor = Cursor::new(ByteBuffer::new());
260 self.write_le(&mut cursor).ok()?;
261
262 buffer.extend_from_slice(
263 &cursor
264 .into_inner()
265 .iter()
266 .map(|x| *x ^ GEARSET_KEY)
267 .collect::<Vec<_>>(),
268 );
269 }
270
271 Some(buffer)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use std::fs::read;
278 use std::path::PathBuf;
279
280 use super::*;
281
282 #[test]
283 fn test_invalid() {
284 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
285 d.push("resources/tests");
286 d.push("random");
287
288 GearSets::from_existing(&read(d).unwrap());
290 }
291
292 fn common_setup(name: &str) -> GearSets {
293 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
294 d.push("resources/tests/gearsets");
295 d.push(name);
296
297 GearSets::from_existing(&read(&d).unwrap()).unwrap()
298 }
299
300 #[test]
301 fn read_simple() {
302 let gearsets = common_setup("simple.dat");
303
304 assert_eq!(gearsets.current_gearset, 0);
305 let gearset = gearsets.gearsets[0].as_ref().unwrap();
306 for i in 1..gearsets.gearsets.len() {
307 assert!(gearsets.gearsets[i].is_none());
308 }
309
310 assert_eq!(gearset.index, 0);
311 assert_eq!(gearset.name, "White Mage");
312 assert!(gearset.facewear.is_none());
313 assert_eq!(gearset.slots.len(), 2);
314 let slot = gearset.slots.get(&GearSlotType::MainHand).unwrap();
315 assert_eq!(slot.id, 5269);
316 assert_eq!(slot.glamour_id, Some(2453));
317 let slot = gearset.slots.get(&GearSlotType::Body).unwrap();
318 assert_eq!(slot.id, 8395913);
319 assert_eq!(slot.glamour_id, None);
320 }
321
322 #[test]
323 fn write_simple() {
324 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
325 d.push("resources/tests/gearsets");
326 d.push("simple.dat");
327
328 let gearset_bytes = &read(d).unwrap();
329 let gearset = GearSets::from_existing(gearset_bytes).unwrap();
330 assert_eq!(*gearset_bytes, gearset.write_to_buffer().unwrap());
331 }
332}