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