Skip to main content

mila/
asset_binary.rs

1use crate::{ArchiveError, BinArchive, BinArchiveReader, BinArchiveWriter};
2
3type Result<T> = std::result::Result<T, ArchiveError>;
4
5// Credits to AmbiguousPresence for the original implementation.
6#[derive(Default, Debug, Clone)]
7pub struct AssetSpec {
8    pub name: Option<String>,
9    pub conditional1: Option<String>,
10    pub conditional2: Option<String>,
11    pub body_model: Option<String>,
12    pub body_texture: Option<String>,
13    pub head_model: Option<String>,
14    pub head_texture: Option<String>,
15    pub hair_model: Option<String>,
16
17    pub hair_texture: Option<String>,
18    pub outer_clothing_model: Option<String>,
19    pub outer_clothing_texture: Option<String>,
20    pub underwear_model: Option<String>,
21    pub underwear_texture: Option<String>,
22    pub mount_model: Option<String>,
23    pub mount_texture: Option<String>,
24    pub mount_outer_clothing_model: Option<String>,
25
26    pub mount_outer_clothing_texture: Option<String>,
27    pub weapon_model_dual: Option<String>,
28    pub weapon_model: Option<String>,
29    pub skeleton: Option<String>,
30    pub mount_skeleton: Option<String>,
31    pub accessory1_model: Option<String>,
32    pub accessory1_texture: Option<String>,
33    pub accessory2_model: Option<String>,
34
35    pub accessory2_texture: Option<String>,
36    pub accessory3_model: Option<String>,
37    pub accessory3_texture: Option<String>,
38    pub attack_animation: Option<String>,
39    pub attack_animation2: Option<String>,
40    pub visual_effect: Option<String>,
41    pub hid: Option<String>,
42    pub footstep_sound: Option<String>,
43
44    pub clothing_sound: Option<String>,
45    pub voice: Option<String>,
46    pub hair_color: [u8; 4],
47    pub use_hair_color: bool,
48    pub skin_color: [u8; 4],
49    pub use_skin_color: bool,
50    pub weapon_trail_color: [u8; 4],
51    pub use_weapon_trail_color: bool,
52    pub model_size: f32,
53    pub use_model_size: bool,
54    pub head_size: f32,
55    pub use_head_size: bool,
56    pub pupil_y: f32,
57    pub use_pupil_y: bool,
58
59    pub unk3: u32,
60    pub use_unk3: bool,
61    pub unk4: u32,
62    pub use_unk4: bool,
63    pub unk5: u32,
64    pub use_unk5: bool,
65    pub unk6: u32,
66    pub use_unk6: bool,
67    pub on_hit_effect: u32,
68    pub use_on_hit_effect: bool,
69    pub unk7: u32,
70    pub use_unk7: bool,
71    pub unk8: u32,
72    pub use_unk8: bool,
73    pub unk9: u32,
74    pub use_unk9: bool,
75
76    pub unk10: u32,
77    pub use_unk10: bool,
78    pub unk11: u32,
79    pub use_unk11: bool,
80    pub unk12: u32,
81    pub use_unk12: bool,
82    pub unk13: u32,
83    pub use_unk13: bool,
84}
85
86fn read_flag_str(
87    reader: &mut BinArchiveReader,
88    flags: &[u8],
89    index: usize,
90) -> Result<Option<String>> {
91    let byte = index / 8;
92    let bit_index = index % 8;
93    if byte >= flags.len() || (flags[byte] & (1 << bit_index)) == 0 {
94        Ok(None)
95    } else {
96        reader.read_string()
97    }
98}
99
100fn write_flag_str(writer: &mut BinArchiveWriter, value: &Option<String>) -> Result<()> {
101    match value {
102        Some(value) => {
103            writer.write_string(Some(value))?;
104        }
105        None => {}
106    }
107    Ok(())
108}
109
110fn read_color(reader: &mut BinArchiveReader) -> Result<[u8; 4]> {
111    let mut arr: [u8; 4] = [0, 0, 0, 0];
112    let bytes = reader.read_bytes(4)?;
113    arr.copy_from_slice(&bytes);
114    Ok(arr)
115}
116
117fn count_bits(byte: u8) -> usize {
118    let mut count = 0;
119    for i in 0..8 {
120        if (byte & (1 << i)) != 0 {
121            count += 1;
122        }
123    }
124    count
125}
126
127impl AssetSpec {
128    pub fn new() -> Self {
129        AssetSpec {
130            ..Default::default()
131        }
132    }
133
134    pub fn from_stream(reader: &mut BinArchiveReader) -> Result<Self> {
135        let mut flag_count = 3;
136        let raw = reader.read_u8()?;
137        if (raw & 0b1) == 1 {
138            flag_count += 4;
139        }
140        let mut flags = vec![raw];
141        flags.extend(reader.read_bytes(flag_count)?);
142
143        let mut spec = AssetSpec::new();
144        spec.name = reader.read_string()?;
145        spec.conditional1 = read_flag_str(reader, &flags, 1)?;
146        spec.conditional2 = read_flag_str(reader, &flags, 2)?;
147        spec.body_model = read_flag_str(reader, &flags, 3)?;
148        spec.body_texture = read_flag_str(reader, &flags, 4)?;
149        spec.head_model = read_flag_str(reader, &flags, 5)?;
150        spec.head_texture = read_flag_str(reader, &flags, 6)?;
151        spec.hair_model = read_flag_str(reader, &flags, 7)?;
152
153        spec.hair_texture = read_flag_str(reader, &flags, 8)?;
154        spec.outer_clothing_model = read_flag_str(reader, &flags, 9)?;
155        spec.outer_clothing_texture = read_flag_str(reader, &flags, 10)?;
156        spec.underwear_model = read_flag_str(reader, &flags, 11)?;
157        spec.underwear_texture = read_flag_str(reader, &flags, 12)?;
158        spec.mount_model = read_flag_str(reader, &flags, 13)?;
159        spec.mount_texture = read_flag_str(reader, &flags, 14)?;
160        spec.mount_outer_clothing_model = read_flag_str(reader, &flags, 15)?;
161
162        spec.mount_outer_clothing_texture = read_flag_str(reader, &flags, 16)?;
163        spec.weapon_model_dual = read_flag_str(reader, &flags, 17)?;
164        spec.weapon_model = read_flag_str(reader, &flags, 18)?;
165        spec.skeleton = read_flag_str(reader, &flags, 19)?;
166        spec.mount_skeleton = read_flag_str(reader, &flags, 20)?;
167        spec.accessory1_model = read_flag_str(reader, &flags, 21)?;
168        spec.accessory1_texture = read_flag_str(reader, &flags, 22)?;
169        spec.accessory2_model = read_flag_str(reader, &flags, 23)?;
170
171        spec.accessory2_texture = read_flag_str(reader, &flags, 24)?;
172        spec.accessory3_model = read_flag_str(reader, &flags, 25)?;
173        spec.accessory3_texture = read_flag_str(reader, &flags, 26)?;
174        spec.attack_animation = read_flag_str(reader, &flags, 27)?;
175        spec.attack_animation2 = read_flag_str(reader, &flags, 28)?;
176        spec.visual_effect = read_flag_str(reader, &flags, 29)?;
177        spec.hid = read_flag_str(reader, &flags, 30)?;
178        spec.footstep_sound = read_flag_str(reader, &flags, 31)?;
179
180        if flag_count > 3 {
181            spec.clothing_sound = read_flag_str(reader, &flags, 32)?;
182            spec.voice = read_flag_str(reader, &flags, 33)?;
183            if (flags[4] & 0b100) != 0 {
184                spec.use_hair_color = true;
185                spec.hair_color = read_color(reader)?;
186            }
187            if (flags[4] & 0b1000) != 0 {
188                spec.use_skin_color = true;
189                spec.skin_color = read_color(reader)?;
190            }
191            if (flags[4] & 0b10000) != 0 {
192                spec.use_weapon_trail_color = true;
193                spec.weapon_trail_color = read_color(reader)?;
194            }
195            if (flags[4] & 0b100000) != 0 {
196                spec.use_model_size = true;
197                spec.model_size = reader.read_f32()?;
198            }
199            if (flags[4] & 0b1000000) != 0 {
200                spec.use_head_size = true;
201                spec.head_size = reader.read_f32()?;
202            }
203            if (flags[4] & 0b10000000) != 0 {
204                spec.use_pupil_y = true;
205                spec.pupil_y = reader.read_f32()?;
206            }
207            if (flags[5] & 0b1) != 0 {
208                spec.unk3 = reader.read_u32()?;
209                spec.use_unk3 = true;
210            }
211            if (flags[5] & 0b10) != 0 {
212                spec.unk4 = reader.read_u32()?;
213                spec.use_unk4 = true;
214            }
215            if (flags[5] & 0b100) != 0 {
216                spec.unk5 = reader.read_u32()?;
217                spec.use_unk5 = true;
218            }
219            if (flags[5] & 0b1000) != 0 {
220                spec.unk6 = reader.read_u32()?;
221                spec.use_unk6 = true;
222            }
223            if (flags[5] & 0b10000) != 0 {
224                spec.on_hit_effect = reader.read_u32()?;
225                spec.use_on_hit_effect = true;
226            }
227            if (flags[5] & 0b100000) != 0 {
228                spec.unk7 = reader.read_u32()?;
229                spec.use_unk7 = true;
230            }
231            if (flags[5] & 0b1000000) != 0 {
232                spec.unk8 = reader.read_u32()?;
233                spec.use_unk8 = true;
234            }
235            if (flags[5] & 0b10000000) != 0 {
236                spec.unk9 = reader.read_u32()?;
237                spec.use_unk9 = true;
238            }
239            if (flags[6] & 0b1) != 0 {
240                spec.unk10 = reader.read_u32()?;
241                spec.use_unk10 = true;
242            }
243            if (flags[6] & 0b10) != 0 {
244                spec.unk11 = reader.read_u32()?;
245                spec.use_unk11 = true;
246            }
247            if (flags[6] & 0b100) != 0 {
248                spec.unk12 = reader.read_u32()?;
249                spec.use_unk12 = true;
250            }
251            if (flags[6] & 0b1000) != 0 {
252                spec.unk13 = reader.read_u32()?;
253                spec.use_unk13 = true;
254            }
255        }
256
257        Ok(spec)
258    }
259
260    fn compute_flags(&self) -> (Vec<u8>, usize) {
261        let mut flags: Vec<u8> = vec![0; 8];
262        flags[0] |= if self.conditional1.is_none() { 0 } else { 0b10 };
263        flags[0] |= if self.conditional2.is_none() {
264            0
265        } else {
266            0b100
267        };
268        flags[0] |= if self.body_model.is_none() { 0 } else { 0b1000 };
269        flags[0] |= if self.body_texture.is_none() {
270            0
271        } else {
272            0b10000
273        };
274        flags[0] |= if self.head_model.is_none() {
275            0
276        } else {
277            0b100000
278        };
279        flags[0] |= if self.head_texture.is_none() {
280            0
281        } else {
282            0b1000000
283        };
284        flags[0] |= if self.hair_model.is_none() {
285            0
286        } else {
287            0b10000000
288        };
289
290        flags[1] |= if self.hair_texture.is_none() { 0 } else { 0b1 };
291        flags[1] |= if self.outer_clothing_model.is_none() {
292            0
293        } else {
294            0b10
295        };
296        flags[1] |= if self.outer_clothing_texture.is_none() {
297            0
298        } else {
299            0b100
300        };
301        flags[1] |= if self.underwear_model.is_none() {
302            0
303        } else {
304            0b1000
305        };
306        flags[1] |= if self.underwear_texture.is_none() {
307            0
308        } else {
309            0b10000
310        };
311        flags[1] |= if self.mount_model.is_none() {
312            0
313        } else {
314            0b100000
315        };
316        flags[1] |= if self.mount_texture.is_none() {
317            0
318        } else {
319            0b1000000
320        };
321        flags[1] |= if self.mount_outer_clothing_model.is_none() {
322            0
323        } else {
324            0b10000000
325        };
326
327        flags[2] |= if self.mount_outer_clothing_texture.is_none() {
328            0
329        } else {
330            0b1
331        };
332        flags[2] |= if self.weapon_model_dual.is_none() {
333            0
334        } else {
335            0b10
336        };
337        flags[2] |= if self.weapon_model.is_none() {
338            0
339        } else {
340            0b100
341        };
342        flags[2] |= if self.skeleton.is_none() { 0 } else { 0b1000 };
343        flags[2] |= if self.mount_skeleton.is_none() {
344            0
345        } else {
346            0b10000
347        };
348        flags[2] |= if self.accessory1_model.is_none() {
349            0
350        } else {
351            0b100000
352        };
353        flags[2] |= if self.accessory1_texture.is_none() {
354            0
355        } else {
356            0b1000000
357        };
358        flags[2] |= if self.accessory2_model.is_none() {
359            0
360        } else {
361            0b10000000
362        };
363
364        flags[3] |= if self.accessory2_texture.is_none() {
365            0
366        } else {
367            0b1
368        };
369        flags[3] |= if self.accessory3_model.is_none() {
370            0
371        } else {
372            0b10
373        };
374        flags[3] |= if self.accessory3_texture.is_none() {
375            0
376        } else {
377            0b100
378        };
379        flags[3] |= if self.attack_animation.is_none() {
380            0
381        } else {
382            0b1000
383        };
384        flags[3] |= if self.attack_animation2.is_none() {
385            0
386        } else {
387            0b10000
388        };
389        flags[3] |= if self.visual_effect.is_none() {
390            0
391        } else {
392            0b100000
393        };
394        flags[3] |= if self.hid.is_none() { 0 } else { 0b1000000 };
395        flags[3] |= if self.footstep_sound.is_none() {
396            0
397        } else {
398            0b10000000
399        };
400
401        flags[4] |= if self.clothing_sound.is_none() {
402            0
403        } else {
404            0b1
405        };
406        flags[4] |= if self.voice.is_none() { 0 } else { 0b10 };
407        flags[4] |= if !self.use_hair_color { 0 } else { 0b100 };
408        flags[4] |= if !self.use_skin_color { 0 } else { 0b1000 };
409        flags[4] |= if !self.use_weapon_trail_color {
410            0
411        } else {
412            0b10000
413        };
414        flags[4] |= if !self.use_model_size { 0 } else { 0b100000 };
415        flags[4] |= if !self.use_head_size { 0 } else { 0b1000000 };
416        flags[4] |= if !self.use_pupil_y { 0 } else { 0b10000000 };
417
418        flags[5] |= if !self.use_unk3 { 0 } else { 0b1 };
419        flags[5] |= if !self.use_unk4 { 0 } else { 0b10 };
420        flags[5] |= if !self.use_unk5 { 0 } else { 0b100 };
421        flags[5] |= if !self.use_unk6 { 0 } else { 0b1000 };
422        flags[5] |= if !self.use_on_hit_effect { 0 } else { 0b10000 };
423        flags[5] |= if !self.use_unk7 { 0 } else { 0b100000 };
424        flags[5] |= if !self.use_unk8 { 0 } else { 0b1000000 };
425        flags[5] |= if !self.use_unk9 { 0 } else { 0b10000000 };
426
427        flags[6] |= if !self.use_unk10 { 0 } else { 0b1 };
428        flags[6] |= if !self.use_unk11 { 0 } else { 0b10 };
429        flags[6] |= if !self.use_unk12 { 0 } else { 0b100 };
430        flags[6] |= if !self.use_unk13 { 0 } else { 0b1000 };
431
432        if flags[4] == 0 && flags[5] == 0 && flags[6] == 0 {
433            flags.resize(4, 0);
434        }
435        let mut size = flags.len() + 4;
436        for flag in &flags {
437            size += count_bits(*flag) * 4;
438        }
439        if flags.len() > 4 {
440            flags[0] |= 1;
441        }
442        (flags, size)
443    }
444
445    pub fn append(&self, archive: &mut BinArchive) -> Result<()> {
446        let (flags, size) = self.compute_flags();
447        let address = archive.size();
448        archive.allocate_at_end(size);
449        let mut writer = BinArchiveWriter::new(archive, address);
450        writer.write_bytes(&flags)?;
451        writer.write_string(self.name.as_deref())?;
452
453        write_flag_str(&mut writer, &self.conditional1)?;
454        write_flag_str(&mut writer, &self.conditional2)?;
455        write_flag_str(&mut writer, &self.body_model)?;
456        write_flag_str(&mut writer, &self.body_texture)?;
457        write_flag_str(&mut writer, &self.head_model)?;
458        write_flag_str(&mut writer, &self.head_texture)?;
459        write_flag_str(&mut writer, &self.hair_model)?;
460
461        write_flag_str(&mut writer, &self.hair_texture)?;
462        write_flag_str(&mut writer, &self.outer_clothing_model)?;
463        write_flag_str(&mut writer, &self.outer_clothing_texture)?;
464        write_flag_str(&mut writer, &self.underwear_model)?;
465        write_flag_str(&mut writer, &self.underwear_texture)?;
466        write_flag_str(&mut writer, &self.mount_model)?;
467        write_flag_str(&mut writer, &self.mount_texture)?;
468        write_flag_str(&mut writer, &self.mount_outer_clothing_model)?;
469
470        write_flag_str(&mut writer, &self.mount_outer_clothing_texture)?;
471        write_flag_str(&mut writer, &self.weapon_model_dual)?;
472        write_flag_str(&mut writer, &self.weapon_model)?;
473        write_flag_str(&mut writer, &self.skeleton)?;
474        write_flag_str(&mut writer, &self.mount_skeleton)?;
475        write_flag_str(&mut writer, &self.accessory1_model)?;
476        write_flag_str(&mut writer, &self.accessory1_texture)?;
477        write_flag_str(&mut writer, &self.accessory2_model)?;
478
479        write_flag_str(&mut writer, &self.accessory2_texture)?;
480        write_flag_str(&mut writer, &self.accessory3_model)?;
481        write_flag_str(&mut writer, &self.accessory3_texture)?;
482        write_flag_str(&mut writer, &self.attack_animation)?;
483        write_flag_str(&mut writer, &self.attack_animation2)?;
484        write_flag_str(&mut writer, &self.visual_effect)?;
485        write_flag_str(&mut writer, &self.hid)?;
486        write_flag_str(&mut writer, &self.footstep_sound)?;
487
488        if flags.len() > 4 {
489            write_flag_str(&mut writer, &self.clothing_sound)?;
490            write_flag_str(&mut writer, &self.voice)?;
491            if self.use_hair_color {
492                writer.write_bytes(&self.hair_color)?;
493            }
494            if self.use_skin_color {
495                writer.write_bytes(&self.skin_color)?;
496            }
497            if self.use_weapon_trail_color {
498                writer.write_bytes(&self.weapon_trail_color)?;
499            }
500            if self.use_model_size {
501                writer.write_f32(self.model_size)?;
502            }
503            if self.use_head_size {
504                writer.write_f32(self.head_size)?;
505            }
506            if self.use_pupil_y {
507                writer.write_f32(self.pupil_y)?;
508            }
509            if self.use_unk3 {
510                writer.write_u32(self.unk3)?;
511            }
512            if self.use_unk4 {
513                writer.write_u32(self.unk4)?;
514            }
515            if self.use_unk5 {
516                writer.write_u32(self.unk5)?;
517            }
518            if self.use_unk6 {
519                writer.write_u32(self.unk6)?;
520            }
521            if self.use_on_hit_effect {
522                writer.write_u32(self.on_hit_effect)?;
523            }
524            if self.use_unk7 {
525                writer.write_u32(self.unk7)?;
526            }
527            if self.use_unk8 {
528                writer.write_u32(self.unk8)?;
529            }
530            if self.use_unk9 {
531                writer.write_u32(self.unk9)?;
532            }
533            if self.use_unk10 {
534                writer.write_u32(self.unk10)?;
535            }
536            if self.use_unk11 {
537                writer.write_u32(self.unk11)?;
538            }
539            if self.use_unk12 {
540                writer.write_u32(self.unk12)?;
541            }
542            if self.use_unk13 {
543                writer.write_u32(self.unk13)?;
544            }
545        }
546        Ok(())
547    }
548}
549
550pub struct AssetBinary {
551    pub flags: u32,
552    pub specs: Vec<AssetSpec>,
553}
554
555impl AssetBinary {
556    pub fn new() -> Self {
557        AssetBinary { flags: 0, specs: Vec::new() }
558    }
559
560    pub fn from_archive(archive: &BinArchive) -> Result<Self> {
561        let mut binary = AssetBinary::new();
562        let mut reader = BinArchiveReader::new(archive, 0);
563        binary.flags = reader.read_u32()?;
564
565        // Read until we hit a malformed spec or EOF.
566        let mut error = false;
567        while !error {
568            match AssetSpec::from_stream(&mut reader) {
569                Ok(spec) => {
570                    binary.specs.push(spec);
571                }
572                Err(_) => {
573                    error = true;
574                }
575            }
576        }
577        Ok(binary)
578    }
579
580    pub fn serialize(&self) -> Result<Vec<u8>> {
581        let mut archive = BinArchive::new();
582        archive.allocate_at_end(4);
583        archive.write_u32(0, self.flags)?;
584        for spec in &self.specs {
585            spec.append(&mut archive)?;
586        }
587        archive.allocate_at_end(4);
588        archive.serialize()
589    }
590}
591
592#[cfg(test)]
593mod test {
594    use super::*;
595    use crate::utils::load_test_file;
596
597    #[test]
598    fn round_trip() {
599        let file = load_test_file("AssetBinary_Test.bin");
600        let archive = BinArchive::from_bytes(&file).unwrap();
601        let asset_binary = AssetBinary::from_archive(&archive);
602        assert!(asset_binary.is_ok());
603        let asset_binary = asset_binary.unwrap();
604        let bytes = asset_binary.serialize().unwrap();
605        assert_eq!(file, bytes);
606    }
607}