1#![allow(dead_code)]
33
34pub mod bin_gff;
35
36use base64ct::{Base64, Encoding};
37use encoding_rs::WINDOWS_1252;
38use std::collections::HashMap;
39use std::mem;
40use std::rc::Rc;
41
42use crate::parsing::*;
43use serde::{Deserialize, Serialize};
44
45#[derive(Debug, Serialize, Deserialize)]
97pub struct Struct {
98 pub id: u32,
102 pub fields: Vec<Field>,
106}
107impl Struct {
108 pub fn len(&self) -> usize {
110 self.fields.len()
111 }
112
113 pub fn is_empty(&self) -> bool {
114 self.fields.is_empty()
115 }
116
117 pub fn get(&self, index: usize) -> Option<&Field> {
119 self.fields.get(index)
120 }
121 pub fn get_mut(&mut self, index: usize) -> Option<&mut Field> {
123 self.fields.get_mut(index)
124 }
125 pub fn find_field(&self, label: &str) -> Option<&Field> {
129 self.fields.iter().find(|f| *f.label == label)
130 }
131 pub fn find(&self, label: &str) -> Option<&FieldValue> {
133 Some(&self.find_field(label)?.value)
134 }
135 pub fn find_field_mut(&mut self, label: &str) -> Option<&mut Field> {
139 self.fields.iter_mut().find(|f| *f.label == label)
140 }
141 pub fn find_mut(&mut self, label: &str) -> Option<&mut FieldValue> {
143 Some(&mut self.find_field_mut(label)?.value)
144 }
145
146 pub fn find_all(&self, label: &str) -> Vec<&FieldValue> {
148 self.fields
149 .iter()
150 .filter(|f| *f.label == label)
151 .map(|f| &f.value)
152 .collect()
153 }
154 pub fn find_all_mut(&mut self, label: &str) -> Vec<&mut FieldValue> {
156 self.fields
157 .iter_mut()
158 .filter(|f| *f.label == label)
159 .map(|f| &mut f.value)
160 .collect()
161 }
162
163 pub fn to_string_pretty(&self, child_indent: &str) -> String {
168 let mut ret = format!(
169 "(struct id={} len={})",
170 if self.id != u32::MAX {
171 self.id as i64
172 } else {
173 -1
174 },
175 self.len()
176 );
177 if !self.is_empty() {
178 ret += "\n";
179 ret += &self
180 .fields
181 .iter()
182 .map(|f| child_indent.to_string() + "├╴ " + &f.to_string_pretty(child_indent))
183 .collect::<Vec<_>>()
184 .join("\n");
185 }
186 ret
187 }
188}
189
190#[derive(Debug, Serialize, Deserialize)]
192pub struct Field {
193 pub label: Rc<FixedSizeString<16>>,
195 #[serde(flatten)]
197 pub value: FieldValue,
198}
199impl Field {
200 pub fn to_string_pretty(&self, child_indent: &str) -> String {
205 format!(
206 "{} = {}",
207 self.label,
208 self.value.to_string_pretty(child_indent)
209 )
210 }
211}
212
213#[derive(Debug, Serialize, Deserialize)]
215#[serde(tag = "type", content = "value", rename_all = "lowercase")]
216pub enum FieldValue {
217 Byte(u8),
219 Char(i8),
221 Word(u16),
223 Short(i16),
225 DWord(u32),
227 Int(i32),
229 DWord64(u64),
231 Int64(i64),
233 Float(f32),
235 Double(f64),
237 #[serde(rename = "cexostr")]
239 String(String),
240 ResRef(String),
242 #[serde(rename = "cexolocstr")]
244 LocString(LocString),
245 #[serde(
247 serialize_with = "serialize_base64",
248 deserialize_with = "deserialize_base64"
249 )]
250 Void(Vec<u8>),
251 Struct(Struct),
253 List(List),
255}
256
257#[derive(Debug, Serialize, Deserialize)]
259pub struct List(Vec<Struct>);
260impl std::ops::Deref for List {
261 type Target = Vec<Struct>;
262 fn deref(&self) -> &Vec<Struct> {
263 &self.0
264 }
265}
266impl std::ops::DerefMut for List {
267 fn deref_mut(&mut self) -> &mut Vec<Struct> {
268 &mut self.0
269 }
270}
271impl List {
272 pub fn to_string_pretty(&self, child_indent: &str) -> String {
277 let content_indent = child_indent.to_string() + "| ";
278 let mut ret = format!("(list len={})", self.len());
279 if self.len() > 0 {
280 ret += "\n";
281 ret += &self
282 .iter()
283 .map(|f| child_indent.to_string() + "├╴ " + &f.to_string_pretty(&content_indent))
284 .collect::<Vec<_>>()
285 .join("\n");
286 }
287 ret
288 }
289}
290
291impl FieldValue {
292 pub fn get_type_str(&self) -> &'static str {
294 match self {
295 FieldValue::Byte(_) => "byte",
296 FieldValue::Char(_) => "char",
297 FieldValue::Word(_) => "word",
298 FieldValue::Short(_) => "short",
299 FieldValue::DWord(_) => "dword",
300 FieldValue::Int(_) => "int",
301 FieldValue::DWord64(_) => "dword64",
302 FieldValue::Int64(_) => "int64",
303 FieldValue::Float(_) => "float",
304 FieldValue::Double(_) => "double",
305 FieldValue::String(_) => "cexostr",
306 FieldValue::ResRef(_) => "resref",
307 FieldValue::LocString(_) => "cexolocstr",
308 FieldValue::Void(_) => "void",
309 FieldValue::Struct(_) => "struct",
310 FieldValue::List(_) => "list",
311 }
312 }
313
314 pub fn to_string_pretty(&self, child_indent: &str) -> String {
319 match self {
320 FieldValue::Byte(v) => format!("{} (byte)", v),
321 FieldValue::Char(v) => format!("{} (char)", v),
322 FieldValue::Word(v) => format!("{} (word)", v),
323 FieldValue::Short(v) => format!("{} (short)", v),
324 FieldValue::DWord(v) => format!("{} (dword)", v),
325 FieldValue::Int(v) => format!("{} (int)", v),
326 FieldValue::DWord64(v) => format!("{} (dword64)", v),
327 FieldValue::Int64(v) => format!("{} (int64)", v),
328 FieldValue::Float(v) => format!("{} (float)", v),
329 FieldValue::Double(v) => format!("{} (double)", v),
330 FieldValue::String(v) => format!("{:?} (cexostr)", v),
331 FieldValue::ResRef(v) => format!("{:?} (resref)", v),
332 FieldValue::LocString(v) => format!("{:?} (cexolocstr)", v),
333 FieldValue::Void(v) => format!("{:?} (void)", Base64::encode_string(&v)),
334 FieldValue::Struct(v) => v.to_string_pretty(&(child_indent.to_string() + "| ")),
335 FieldValue::List(v) => v.to_string_pretty(&(child_indent.to_string() + "| ")),
336 }
337 }
338
339 pub fn unwrap_byte(&self) -> u8 {
341 match self {
342 FieldValue::Byte(v) => *v,
343 _ => panic!("not a GFF Byte"),
344 }
345 }
346 pub fn unwrap_char(&self) -> i8 {
348 match self {
349 FieldValue::Char(v) => *v,
350 _ => panic!("not a GFF Char"),
351 }
352 }
353 pub fn unwrap_word(&self) -> u16 {
355 match self {
356 FieldValue::Word(v) => *v,
357 _ => panic!("not a GFF Word"),
358 }
359 }
360 pub fn unwrap_short(&self) -> i16 {
362 match self {
363 FieldValue::Short(v) => *v,
364 _ => panic!("not a GFF Short"),
365 }
366 }
367 pub fn unwrap_dword(&self) -> u32 {
369 match self {
370 FieldValue::DWord(v) => *v,
371 _ => panic!("not a GFF DWord"),
372 }
373 }
374 pub fn unwrap_int(&self) -> i32 {
376 match self {
377 FieldValue::Int(v) => *v,
378 _ => panic!("not a GFF Int"),
379 }
380 }
381 pub fn unwrap_dword64(&self) -> u64 {
383 match self {
384 FieldValue::DWord64(v) => *v,
385 _ => panic!("not a GFF DWord64"),
386 }
387 }
388 pub fn unwrap_int64(&self) -> i64 {
390 match self {
391 FieldValue::Int64(v) => *v,
392 _ => panic!("not a GFF Int64"),
393 }
394 }
395 pub fn unwrap_float(&self) -> f32 {
397 match self {
398 FieldValue::Float(v) => *v,
399 _ => panic!("not a GFF Int64"),
400 }
401 }
402 pub fn unwrap_double(&self) -> f64 {
404 match self {
405 FieldValue::Double(v) => *v,
406 _ => panic!("not a GFF Int64"),
407 }
408 }
409 pub fn unwrap_string(&self) -> &String {
411 match self {
412 FieldValue::String(v) => v,
413 _ => panic!("not a GFF String"),
414 }
415 }
416 pub fn unwrap_resref(&self) -> &String {
418 match self {
419 FieldValue::ResRef(v) => v,
420 _ => panic!("not a GFF ResRef"),
421 }
422 }
423 pub fn unwrap_locstring(&self) -> &LocString {
425 match self {
426 FieldValue::LocString(v) => v,
427 _ => panic!("not a GFF LocString"),
428 }
429 }
430 pub fn unwrap_void(&self) -> &Vec<u8> {
432 match self {
433 FieldValue::Void(v) => v,
434 _ => panic!("not a GFF Void"),
435 }
436 }
437 pub fn unwrap_struct(&self) -> &Struct {
439 match self {
440 FieldValue::Struct(v) => v,
441 _ => panic!("not a GFF Struct"),
442 }
443 }
444 pub fn unwrap_list(&self) -> &Vec<Struct> {
446 match self {
447 FieldValue::List(v) => v,
448 _ => panic!("not a GFF List"),
449 }
450 }
451}
452
453#[derive(Debug, Serialize, Deserialize)]
455pub struct LocString {
456 pub strref: u32,
458 pub strings: Vec<(i32, String)>,
460}
461
462#[derive(Debug, Serialize, Deserialize)]
464pub struct Gff {
465 pub file_type: FixedSizeString<4>,
467 pub file_version: FixedSizeString<4>,
469 pub root: Struct,
471}
472impl Gff {
473 pub fn from_bytes(input: &[u8]) -> NWNParseResult<Self> {
475 let (input, bingff) = bin_gff::Gff::from_bytes(input)?;
476 Ok((
477 input,
478 Self::from_bin_gff(&bingff).expect("should have been checked while parsing bingff"),
479 ))
480 }
481
482 pub fn from_bin_gff(bingff: &bin_gff::Gff) -> Result<Self, Box<dyn std::error::Error>> {
484 fn build_struct(
485 gff_struct: &bin_gff::Struct,
486 bingff: &bin_gff::Gff,
487 labels: &Vec<Rc<FixedSizeString<16>>>,
488 ) -> Struct {
489 let fields = gff_struct
490 .get_field_indices(&bingff.field_indices)
491 .unwrap()
492 .iter()
493 .map(|field_index| {
494 let gff_field = bingff.fields.get(*field_index as usize).unwrap();
496 let label = labels.get(gff_field.label_index as usize).unwrap().clone();
499 let value = match gff_field.value {
500 bin_gff::FieldValue::Invalid => panic!(), bin_gff::FieldValue::Byte(value) => FieldValue::Byte(value),
502 bin_gff::FieldValue::Char(value) => FieldValue::Char(value),
503 bin_gff::FieldValue::Word(value) => FieldValue::Word(value),
504 bin_gff::FieldValue::Short(value) => FieldValue::Short(value),
505 bin_gff::FieldValue::DWord(value) => FieldValue::DWord(value),
506 bin_gff::FieldValue::Int(value) => FieldValue::Int(value),
507 bin_gff::FieldValue::DWord64 { data_offset: _ } => FieldValue::DWord64(
508 gff_field.value.get_dword64(&bingff.field_data).unwrap(),
509 ),
510 bin_gff::FieldValue::Int64 { data_offset: _ } => FieldValue::Int64(
511 gff_field.value.get_int64(&bingff.field_data).unwrap(),
512 ),
513 bin_gff::FieldValue::Float(value) => FieldValue::Float(value),
514 bin_gff::FieldValue::Double { data_offset: _ } => FieldValue::Double(
515 gff_field.value.get_double(&bingff.field_data).unwrap(),
516 ),
517 bin_gff::FieldValue::String { data_offset: _ } => FieldValue::String(
518 gff_field
519 .value
520 .get_string(&bingff.field_data)
521 .unwrap()
522 .to_string(),
523 ),
524 bin_gff::FieldValue::ResRef { data_offset: _ } => FieldValue::ResRef(
525 gff_field
526 .value
527 .get_resref(&bingff.field_data)
528 .unwrap()
529 .to_string(),
530 ),
531 bin_gff::FieldValue::LocString { data_offset: _ } => {
532 let gff_locstr =
533 gff_field.value.get_locstring(&bingff.field_data).unwrap();
534 FieldValue::LocString(LocString {
536 strref: gff_locstr.0,
537 strings: gff_locstr
538 .1
539 .into_iter()
540 .map(|ls| (ls.0, ls.1.to_string()))
541 .collect(),
542 })
543 }
544 bin_gff::FieldValue::Void { data_offset: _ } => FieldValue::Void(
545 gff_field
546 .value
547 .get_void(&bingff.field_data)
548 .unwrap()
549 .to_vec(),
550 ),
551 bin_gff::FieldValue::Struct { struct_index } => {
552 FieldValue::Struct(build_struct(
553 bingff.structs.get(struct_index as usize).unwrap(),
554 bingff,
555 labels,
556 ))
557 }
558 bin_gff::FieldValue::List { list_offset: _ } => {
559 let list = gff_field
560 .value
561 .get_list_struct_indices(&bingff.list_indices)
562 .expect("Bad list indices / data");
563 FieldValue::List(List(
564 list.iter()
565 .map(|struct_index| {
566 build_struct(
567 bingff.structs.get(*struct_index as usize).unwrap(),
568 bingff,
569 labels,
570 )
571 })
572 .collect(),
573 ))
574 }
575 };
576 Field { label, value }
577 })
578 .collect::<Vec<_>>();
579
580 Struct {
581 id: gff_struct.id,
582 fields,
583 }
584 }
585
586 let labels = bingff
587 .labels
588 .clone()
589 .into_iter()
590 .map(Rc::new)
591 .collect::<Vec<_>>();
592
593 let root = build_struct(
594 bingff.structs.first().expect("Missing root struct"),
595 bingff,
596 &labels,
597 );
598
599 Ok(Self {
600 file_type: bingff.header.file_type,
601 file_version: bingff.header.file_version,
602 root,
603 })
604 }
605
606 pub fn to_string_pretty(&self) -> String {
608 let mut ret = String::new();
609 ret += &format!(
610 "========== GFF {:?} {:?} ==========",
611 self.file_type, self.file_version
612 );
613 ret += &self.root.to_string_pretty("");
614 ret
615 }
616
617 pub fn to_bin_gff(&self) -> bin_gff::Gff {
619 let mut bingff = bin_gff::Gff {
620 header: bin_gff::Header::new(self.file_type, self.file_version),
621 structs: vec![],
622 fields: vec![],
623 labels: vec![],
624 field_data: vec![],
625 field_indices: vec![],
626 list_indices: vec![],
627 };
628
629 let mut labels_map = HashMap::<String, usize>::new();
630
631 fn register_field(
632 field: &Field,
633 bingff: &mut bin_gff::Gff,
634 labels_map: &mut HashMap<String, usize>,
635 ) -> u32 {
636 let label_index = if let Some(index) = labels_map.get(field.label.as_str()) {
637 *index as u32
638 } else {
639 let index = labels_map.len();
640 bingff.labels.push(*field.label);
641 labels_map.insert(field.label.to_string(), index);
642 index as u32
643 };
644
645 let field_index = bingff.fields.len();
647 bingff.fields.push(bin_gff::Field {
648 label_index,
649 value: bin_gff::FieldValue::Invalid,
650 });
651
652 let value = match &field.value {
654 FieldValue::Byte(v) => bin_gff::FieldValue::Byte(*v),
655 FieldValue::Char(v) => bin_gff::FieldValue::Char(*v),
656 FieldValue::Word(v) => bin_gff::FieldValue::Word(*v),
657 FieldValue::Short(v) => bin_gff::FieldValue::Short(*v),
658 FieldValue::DWord(v) => bin_gff::FieldValue::DWord(*v),
659 FieldValue::Int(v) => bin_gff::FieldValue::Int(*v),
660 FieldValue::DWord64(v) => {
661 let data_offset = bingff.field_data.len() as u32;
662 bingff.field_data.extend(v.to_le_bytes());
663 bin_gff::FieldValue::DWord64 { data_offset }
664 }
665 FieldValue::Int64(v) => {
666 let data_offset = bingff.field_data.len() as u32;
667 bingff.field_data.extend(v.to_le_bytes());
668 bin_gff::FieldValue::Int64 { data_offset }
669 }
670 FieldValue::Float(v) => bin_gff::FieldValue::Float(*v),
671 FieldValue::Double(v) => {
672 let data_offset = bingff.field_data.len() as u32;
673 bingff.field_data.extend(v.to_le_bytes());
674 bin_gff::FieldValue::Double { data_offset }
675 }
676 FieldValue::String(v) => {
677 let data_offset = bingff.field_data.len() as u32;
678 bingff.field_data.extend((v.len() as u32).to_le_bytes());
679 bingff.field_data.extend(v.as_bytes());
680 bin_gff::FieldValue::String { data_offset }
681 }
682 FieldValue::ResRef(v) => {
683 let data_offset = bingff.field_data.len() as u32;
684 let (str_data, _, _) = WINDOWS_1252.encode(v);
685
686 bingff
687 .field_data
688 .extend((str_data.len() as u8).to_le_bytes());
689 bingff.field_data.extend(str_data.as_ref());
690 bin_gff::FieldValue::ResRef { data_offset }
691 }
692 FieldValue::LocString(v) => {
693 let data_offset = bingff.field_data.len() as u32;
694
695 let total_len = mem::size_of::<u32>() * 2
696 + v.strings
697 .iter()
698 .map(|(_lang, text)| mem::size_of::<u32>() * 2 + text.as_bytes().len())
699 .sum::<usize>();
700
701 bingff.field_data.reserve(mem::size_of::<u32>() + total_len);
703
704 bingff.field_data.extend((total_len as u32).to_le_bytes());
705 bingff.field_data.extend(v.strref.to_le_bytes());
706 bingff
707 .field_data
708 .extend((v.strings.len() as u32).to_le_bytes());
709
710 for (id, text) in &v.strings {
711 bingff.field_data.extend(id.to_le_bytes());
712 bingff.field_data.extend((text.len() as u32).to_le_bytes());
713 bingff.field_data.extend(text.as_bytes());
714 }
715
716 bin_gff::FieldValue::LocString { data_offset }
717 }
718 FieldValue::Void(v) => {
719 let data_offset = bingff.field_data.len() as u32;
720 bingff.field_data.extend((v.len() as u32).to_le_bytes());
721 bingff.field_data.extend(v);
722 bin_gff::FieldValue::Void { data_offset }
723 }
724 FieldValue::Struct(v) => {
725 let struct_index = register_struct(v, bingff, labels_map);
726 bin_gff::FieldValue::Struct { struct_index }
727 }
728 FieldValue::List(v) => {
729 let list_offset = (bingff.list_indices.len() * 4) as u32;
730 bingff
731 .list_indices
732 .resize(bingff.list_indices.len() + 1 + v.len(), 0);
733
734 bingff.list_indices[(list_offset / 4) as usize] = v.len() as u32;
736
737 for (i, gffstruct) in v.iter().enumerate() {
739 bingff.list_indices[(list_offset / 4) as usize + 1 + i] =
740 register_struct(gffstruct, bingff, labels_map);
741 }
742
743 bin_gff::FieldValue::List { list_offset }
744 }
745 };
746
747 bingff.fields[field_index].value = value;
748
749 field_index as u32
750 }
751
752 fn register_struct(
753 gffstruct: &Struct,
754 bingff: &mut bin_gff::Gff,
755 labels_map: &mut HashMap<String, usize>,
756 ) -> u32 {
757 let struct_index = bingff.structs.len();
758 bingff.structs.push(bin_gff::Struct {
759 id: gffstruct.id,
760 data_or_data_offset: 0,
761 field_count: gffstruct.len() as u32,
762 });
763
764 bingff.structs[struct_index].data_or_data_offset = match gffstruct.len() {
765 0 => 0,
766 1 => register_field(&gffstruct.fields[0], bingff, labels_map),
767 _cnt => {
768 let dodo = (bingff.field_indices.len() * mem::size_of::<u32>()) as u32;
769
770 bingff
771 .field_indices
772 .resize(bingff.field_indices.len() + gffstruct.fields.len(), 0);
773
774 for (i, field) in gffstruct.fields.iter().enumerate() {
775 let field_index = register_field(field, bingff, labels_map);
776 let field_indices_index = dodo as usize / mem::size_of::<u32>() + i;
777 bingff.field_indices[field_indices_index] = field_index;
778 }
779
780 dodo
781 }
782 };
783
784 struct_index as u32
785 }
786
787 register_struct(&self.root, &mut bingff, &mut labels_map);
788
789 let mut header = bin_gff::Header::new(self.file_type, self.file_version);
791 header.update(&bingff);
792 bingff.header = header;
793
794 bingff
795 }
796
797 pub fn to_bytes(&self) -> Vec<u8> {
799 self.to_bin_gff().to_bytes()
800 }
801}
802
803#[cfg(test)]
804mod tests {
805 use super::*;
806
807 #[test]
808 fn test_gff_read_doge() {
809 let doge_bytes = include_bytes!("../unittest/nwn2_doge.utc");
810 let (_, bingff) = bin_gff::Gff::from_bytes(doge_bytes).unwrap();
811 let (_, bingff_rebuilt) = bin_gff::Gff::from_bytes(&bingff.to_bytes()).unwrap();
812
813 assert_eq!(bingff.to_bytes(), doge_bytes);
814 assert_eq!(bingff_rebuilt.to_bytes(), doge_bytes);
815
816 let gff = Gff::from_bin_gff(&bingff).unwrap();
817
818 assert_eq!(gff.root.find("Subrace").unwrap().unwrap_byte(), 24);
819 assert_eq!(gff.root.find("Appearance_Type").unwrap().unwrap_word(), 181);
821 assert_eq!(gff.root.find("HitPoints").unwrap().unwrap_short(), 9);
822 assert_eq!(gff.root.find("DecayTime").unwrap().unwrap_dword(), 5000);
823 assert_eq!(gff.root.find("WalkRate").unwrap().unwrap_int(), 5);
824 assert!((gff.root.find("ChallengeRating").unwrap().unwrap_float() - 100f32).abs() < 0.001);
827 assert_eq!(gff.root.find("Tag").unwrap().unwrap_string(), "secret_doge");
829 assert_eq!(
830 gff.root.find("ScriptDamaged").unwrap().unwrap_resref(),
831 "nw_c2_default6"
832 );
833
834 let locstr = gff.root.find("Description").unwrap().unwrap_locstring();
835 assert_eq!(locstr.strref, 175081);
836 assert_eq!(
837 &locstr.strings,
838 &[(
839 0,
840 "Une indicible intelligence pétille dans ses yeux fous...\r\nWow...".to_string()
841 )]
842 );
843
844 let gffstruct = gff.root.find("ACRtAnkle").unwrap().unwrap_struct();
847 assert_eq!(gffstruct.id, 0);
848 assert_eq!(gffstruct.fields.len(), 3);
849
850 let gfflist = gff.root.find("FeatList").unwrap().unwrap_list();
851 assert_eq!(gfflist.len(), 2);
852
853 let tint_struct = gff
855 .root
856 .find("Tint_Hair")
857 .unwrap()
858 .unwrap_struct()
859 .find("Tintable")
860 .unwrap()
861 .unwrap_struct()
862 .find("Tint")
863 .unwrap()
864 .unwrap_struct()
865 .find("1")
866 .unwrap()
867 .unwrap_struct();
868 assert_eq!(tint_struct.find("r").unwrap().unwrap_byte(), 247);
869 assert_eq!(tint_struct.find("a").unwrap().unwrap_byte(), 255);
870 assert_eq!(tint_struct.find("b").unwrap().unwrap_byte(), 0);
871 assert_eq!(tint_struct.find("g").unwrap().unwrap_byte(), 223);
872
873 let item_struct = &gff.root.find("Equip_ItemList").unwrap().unwrap_list()[0];
874 assert_eq!(item_struct.id, 16384);
875 assert_eq!(
876 item_struct.find("EquippedRes").unwrap().unwrap_resref(),
877 "nw_it_crewps005"
878 );
879
880 let skill_struct = &gff.root.find("SkillList").unwrap().unwrap_list()[6];
881 assert_eq!(skill_struct.id, 0);
882 assert_eq!(skill_struct.find("Rank").unwrap().unwrap_byte(), 2);
883
884 assert_eq!(gff.to_bytes(), doge_bytes);
886 }
887
888 #[test]
889 fn test_gff_read_krogar() {
890 let krogar_bytes = include_bytes!("../unittest/krogar.bic");
891 let (_, gff) = Gff::from_bytes(krogar_bytes).unwrap();
892
893 assert_eq!(gff.file_type, "BIC ");
894 assert_eq!(gff.file_version, "V3.2");
895
896 assert_eq!(gff.root.id, u32::max_value());
897 assert_eq!(gff.root.find("ACLtElbow").unwrap().unwrap_struct().id, 4);
898
899 assert_eq!(gff.root.find("IsPC").unwrap().unwrap_byte(), 1);
900 assert_eq!(gff.root.find("RefSaveThrow").unwrap().unwrap_char(), 13);
901 assert_eq!(gff.root.find("SoundSetFile").unwrap().unwrap_word(), 363);
902 assert_eq!(gff.root.find("HitPoints").unwrap().unwrap_short(), 320);
903 assert_eq!(gff.root.find("Gold").unwrap().unwrap_dword(), 6400);
904 assert_eq!(gff.root.find("Age").unwrap().unwrap_int(), 50);
905 assert_eq!(gff.root.find("XpMod").unwrap().unwrap_float(), 1f32);
908 assert_eq!(
910 gff.root.find("Deity").unwrap().unwrap_string(),
911 "Gorm Gulthyn"
912 );
913 assert_eq!(
914 gff.root.find("ScriptHeartbeat").unwrap().unwrap_resref(),
915 "gb_player_heart"
916 );
917 let first_name = gff.root.find("FirstName").unwrap().unwrap_locstring();
918 assert_eq!(first_name.strref, u32::max_value());
919 assert_eq!(first_name.strings.len(), 2);
920 assert_eq!(first_name.strings[0], (0, "Krogar".to_string()));
921 assert_eq!(first_name.strings[1], (2, "Krogar".to_string()));
922
923 let lvl_stat_list = gff.root.find("LvlStatList").unwrap().unwrap_list();
924 assert_eq!(lvl_stat_list.len(), 30);
925 assert_eq!(
926 lvl_stat_list
927 .get(5)
928 .unwrap()
929 .find("FeatList")
930 .unwrap()
931 .unwrap_list()
932 .get(0)
933 .unwrap()
934 .find("Feat")
935 .unwrap()
936 .unwrap_word(),
937 389
938 );
939
940 let mut bingff = gff.to_bin_gff();
942 bingff.structs[4804].data_or_data_offset = 4294967295;
945
946 assert_eq!(bingff.to_bytes(), krogar_bytes);
947 }
948
949 #[test]
950 fn test_gff_read_towncrier() {
951 let towncrier_bytes = include_bytes!("../unittest/nwn1_towncrier.utc");
952 let (_, gff) = Gff::from_bytes(towncrier_bytes).unwrap();
953
954 assert_eq!(gff.root.find("Race").unwrap().unwrap_byte(), 3);
955 assert_eq!(gff.root.find("PortraitId").unwrap().unwrap_word(), 957);
957 assert_eq!(gff.root.find("HitPoints").unwrap().unwrap_short(), 70);
958 assert_eq!(gff.root.find("DecayTime").unwrap().unwrap_dword(), 5000);
959 assert_eq!(gff.root.find("WalkRate").unwrap().unwrap_int(), 7);
960 assert!((gff.root.find("ChallengeRating").unwrap().unwrap_float() - 11f32).abs() < 0.001);
963 assert_eq!(gff.root.find("Tag").unwrap().unwrap_string(), "TownCrier");
965 assert_eq!(
966 gff.root.find("ScriptHeartbeat").unwrap().unwrap_resref(),
967 "ohb_towncrier"
968 );
969
970 let locstr = gff.root.find("FirstName").unwrap().unwrap_locstring();
971 assert_eq!(locstr.strref, 12599);
972 assert_eq!(&locstr.strings, &[(0, "Town Crier".to_string())]);
973
974 let gfflist = gff.root.find("FeatList").unwrap().unwrap_list();
977 assert_eq!(gfflist.len(), 20);
978
979 let item_struct = &gff.root.find("ItemList").unwrap().unwrap_list()[1];
981 assert_eq!(item_struct.id, 1);
982 assert_eq!(
983 item_struct.find("InventoryRes").unwrap().unwrap_resref(),
984 "nw_it_torch001"
985 );
986 assert_eq!(item_struct.find("Repos_PosX").unwrap().unwrap_word(), 2);
987 assert_eq!(item_struct.find("Repos_Posy").unwrap().unwrap_word(), 0);
988
989 assert_eq!(gff.to_bytes(), towncrier_bytes);
991 }
992
993 #[test]
994 fn test_gff_araignee() {
995 let araignee_bytes = include_bytes!("../unittest/araignéesouterrains.ute");
996 let (_, gff) = Gff::from_bytes(araignee_bytes).unwrap();
997
998 assert_eq!(gff.to_bytes(), araignee_bytes);
1000 }
1001
1002 extern crate test;
1003 #[bench]
1004 fn bench_krogar(b: &mut test::Bencher) {
1005 b.iter(|| {
1006 let krogar_bytes = include_bytes!("../unittest/krogar.bic");
1007 let (_, _gff) = bin_gff::Gff::from_bytes(krogar_bytes).unwrap();
1008 });
1009 }
1011}