nwn_lib_rs/
gff.rs

1//! GFF file format (.bic, are, .git, .ut?, ...)
2//!
3//! Example
4//! ```rust
5//! #![feature(generic_const_exprs)]
6//! #![allow(incomplete_features)]
7//! use nwn_lib_rs::gff::{Gff, FieldValue};
8//!
9//! // Parse GFF file
10//! let gff_data = std::fs::read("unittest/krogar.bic").unwrap();
11//! let (_, mut gff) = Gff::from_bytes(&gff_data).expect("Failed to parse GFF data");
12//!
13//! // Get IsPC GFF node value
14//! let is_pc = gff.root.find("IsPC").unwrap().unwrap_byte();
15//!
16//! // Get first item in the inventory
17//! let item_struct = &gff.root.find("ItemList").unwrap().unwrap_list()[0];
18//! assert_eq!(
19//!     item_struct.find("TemplateResRef").unwrap().unwrap_resref(),
20//!     "nw_it_contain003"
21//! );
22//!
23//! // Set BumpState
24//! let mut bump_state = gff.root.find_mut("BumpState").unwrap();
25//! *bump_state = FieldValue::Byte(42);
26//!
27//! // Serialize resulting GFF
28//! let gff_data = gff.to_bytes();
29//! ```
30//!
31
32#![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)]
46// pub enum PathNode<'a> {
47//     Label(&'a str),
48//     Index(u32),
49// }
50// impl<'a> From<&'a str> for PathNode<'a> {
51//     fn from(s: &'a str) -> Self {
52//         PathNode::Label(s)
53//     }
54// }
55// impl From<u32> for PathNode<'_> {
56//     fn from(s: u32) -> Self {
57//         PathNode::Index(s)
58//     }
59// }
60
61// #[macro_export]
62// macro_rules! gff_path {
63//     [$($nodes:expr),* $(,)?] => {
64//         &[ $( gff_path!(wrap $nodes),  )* ]
65//     };
66//     (wrap $a:expr) => {
67//         PathNode::from($a)
68//     };
69// }
70
71// pub fn list_find_path<'a>(&'a list: Vec<Struct>, path: &[PathNode]) -> Option<&'a Struct> {
72//     match path.first()? {
73//         PathNode::Label(label) => None,
74//         PathNode::Index(index) => list.get(*index as usize),
75//     }
76// }
77
78// fn get_struct_field_indices<'a>(
79//     offset: &'a u32,
80//     field_count: &u32,
81//     field_indices: &'a [u32],
82// ) -> Option<&'a [u32]> {
83//     if *field_count == 1 {
84//         Some(std::array::from_ref(offset))
85//     } else {
86//         let start = *offset as usize / mem::size_of::<u32>();
87//         let end = start + *field_count as usize;
88//         field_indices.get(start..end)
89//     }
90// }
91
92/// GFF Struct (Key/Value container)
93///
94/// Contains a list of `Field`, with a label string used as a key
95/// Note: in some rare cases, there can be multiple fields with the same label.
96#[derive(Debug, Serialize, Deserialize)]
97pub struct Struct {
98    /// Struct ID
99    ///
100    /// This is sometimes used as the `Struct` index in a `List`, or for identifying a specific `Struct` template
101    pub id: u32,
102    /// List of fields in this struct
103    ///
104    /// Note: in some rare cases, there can be multiple fields with the same label.
105    pub fields: Vec<Field>,
106}
107impl Struct {
108    /// Number of fields in this `Struct`
109    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    /// Returns the nth `Field` in this `Struct`
118    pub fn get(&self, index: usize) -> Option<&Field> {
119        self.fields.get(index)
120    }
121    /// Returns the nth `Field` in this `Struct`
122    pub fn get_mut(&mut self, index: usize) -> Option<&mut Field> {
123        self.fields.get_mut(index)
124    }
125    /// Finds the first `Field` matching the given label
126    ///
127    /// Note: `find` is more useful for most use cases)
128    pub fn find_field(&self, label: &str) -> Option<&Field> {
129        self.fields.iter().find(|f| *f.label == label)
130    }
131    /// Finds the first `FieldValue` matching the given label
132    pub fn find(&self, label: &str) -> Option<&FieldValue> {
133        Some(&self.find_field(label)?.value)
134    }
135    /// Finds the first `Field` matching the given label
136    ///
137    /// Note: `find_mut` is more useful for most use cases)
138    pub fn find_field_mut(&mut self, label: &str) -> Option<&mut Field> {
139        self.fields.iter_mut().find(|f| *f.label == label)
140    }
141    /// Finds the first `FieldValue` matching the given label
142    pub fn find_mut(&mut self, label: &str) -> Option<&mut FieldValue> {
143        Some(&mut self.find_field_mut(label)?.value)
144    }
145
146    /// Accumulates all `FieldValue` that matches the given label (in most cases there's only one field for each label)
147    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    /// Accumulates all `FieldValue` that matches the given label (in most cases there's only one field for each label)
155    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    /// Serializes this struct to human-readable text
164    ///
165    /// # Args:
166    /// * child_indent: prepend this str to each line after the first one
167    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/// GFF Field (field label + `FieldValue` container)
191#[derive(Debug, Serialize, Deserialize)]
192pub struct Field {
193    /// Label string
194    pub label: Rc<FixedSizeString<16>>,
195    /// Field value enum
196    #[serde(flatten)]
197    pub value: FieldValue,
198}
199impl Field {
200    /// Serializes this field to human-readable text
201    ///
202    /// # Args:
203    /// * child_indent: prepend this str to each line after the first one
204    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/// GFF Field value (value node inside a GFF `Struct`)
214#[derive(Debug, Serialize, Deserialize)]
215#[serde(tag = "type", content = "value", rename_all = "lowercase")]
216pub enum FieldValue {
217    /// Unsigned 8-bit integer
218    Byte(u8),
219    /// Signed 8-bit integer
220    Char(i8),
221    /// Unsigned 16-bit integer
222    Word(u16),
223    /// Signed 16-bit integer
224    Short(i16),
225    /// Unsigned 32-bit integer
226    DWord(u32),
227    /// Signed 32-bit integer
228    Int(i32),
229    /// Unsigned 64-bit integer
230    DWord64(u64),
231    /// Signed 64-bit integer
232    Int64(i64),
233    /// 32-bit floating point
234    Float(f32),
235    /// 64-bit floating point
236    Double(f64),
237    /// String, with a length stored as a 32-bit unsigned integer
238    #[serde(rename = "cexostr")]
239    String(String),
240    /// String, with a length stored as a 8-bit unsigned integer
241    ResRef(String),
242    /// Localized strung
243    #[serde(rename = "cexolocstr")]
244    LocString(LocString),
245    /// Raw data
246    #[serde(
247        serialize_with = "serialize_base64",
248        deserialize_with = "deserialize_base64"
249    )]
250    Void(Vec<u8>),
251    /// `Struct` containing other `Field`s
252    Struct(Struct),
253    /// `List` of `Struct`s
254    List(List),
255}
256
257/// GFF List containing multiple `Struct`s
258#[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    /// Serializes this list to human-readable text
273    ///
274    /// # Args:
275    /// * child_indent: prepend this str to each line after the first one
276    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    /// Returns a str representation of the stored type
293    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    /// Serializes this field to human-readable text
315    ///
316    /// # Args:
317    /// * child_indent: prepend this str to each line after the first one
318    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    /// Unwraps stored byte value, panics if the field is not a byte
340    pub fn unwrap_byte(&self) -> u8 {
341        match self {
342            FieldValue::Byte(v) => *v,
343            _ => panic!("not a GFF Byte"),
344        }
345    }
346    /// Unwraps stored char value, panics if the field is not a char
347    pub fn unwrap_char(&self) -> i8 {
348        match self {
349            FieldValue::Char(v) => *v,
350            _ => panic!("not a GFF Char"),
351        }
352    }
353    /// Unwraps stored word value, panics if the field is not a word
354    pub fn unwrap_word(&self) -> u16 {
355        match self {
356            FieldValue::Word(v) => *v,
357            _ => panic!("not a GFF Word"),
358        }
359    }
360    /// Unwraps stored short value, panics if the field is not a short
361    pub fn unwrap_short(&self) -> i16 {
362        match self {
363            FieldValue::Short(v) => *v,
364            _ => panic!("not a GFF Short"),
365        }
366    }
367    /// Unwraps stored dword value, panics if the field is not a dword
368    pub fn unwrap_dword(&self) -> u32 {
369        match self {
370            FieldValue::DWord(v) => *v,
371            _ => panic!("not a GFF DWord"),
372        }
373    }
374    /// Unwraps stored int value, panics if the field is not a int
375    pub fn unwrap_int(&self) -> i32 {
376        match self {
377            FieldValue::Int(v) => *v,
378            _ => panic!("not a GFF Int"),
379        }
380    }
381    /// Unwraps stored dword64 value, panics if the field is not a dword64
382    pub fn unwrap_dword64(&self) -> u64 {
383        match self {
384            FieldValue::DWord64(v) => *v,
385            _ => panic!("not a GFF DWord64"),
386        }
387    }
388    /// Unwraps stored int64 value, panics if the field is not a int64
389    pub fn unwrap_int64(&self) -> i64 {
390        match self {
391            FieldValue::Int64(v) => *v,
392            _ => panic!("not a GFF Int64"),
393        }
394    }
395    /// Unwraps stored float value, panics if the field is not a float
396    pub fn unwrap_float(&self) -> f32 {
397        match self {
398            FieldValue::Float(v) => *v,
399            _ => panic!("not a GFF Int64"),
400        }
401    }
402    /// Unwraps stored double value, panics if the field is not a double
403    pub fn unwrap_double(&self) -> f64 {
404        match self {
405            FieldValue::Double(v) => *v,
406            _ => panic!("not a GFF Int64"),
407        }
408    }
409    /// Unwraps stored string value, panics if the field is not a string
410    pub fn unwrap_string(&self) -> &String {
411        match self {
412            FieldValue::String(v) => v,
413            _ => panic!("not a GFF String"),
414        }
415    }
416    /// Unwraps stored resref value, panics if the field is not a resref
417    pub fn unwrap_resref(&self) -> &String {
418        match self {
419            FieldValue::ResRef(v) => v,
420            _ => panic!("not a GFF ResRef"),
421        }
422    }
423    /// Unwraps stored locstring value, panics if the field is not a locstring
424    pub fn unwrap_locstring(&self) -> &LocString {
425        match self {
426            FieldValue::LocString(v) => v,
427            _ => panic!("not a GFF LocString"),
428        }
429    }
430    /// Unwraps stored void value, panics if the field is not a void
431    pub fn unwrap_void(&self) -> &Vec<u8> {
432        match self {
433            FieldValue::Void(v) => v,
434            _ => panic!("not a GFF Void"),
435        }
436    }
437    /// Unwraps stored struct value, panics if the field is not a struct
438    pub fn unwrap_struct(&self) -> &Struct {
439        match self {
440            FieldValue::Struct(v) => v,
441            _ => panic!("not a GFF Struct"),
442        }
443    }
444    /// Unwraps stored list value, panics if the field is not a list
445    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/// Localized string. Contains a strref + a list of locale-specific strings
454#[derive(Debug, Serialize, Deserialize)]
455pub struct LocString {
456    /// String Ref, to be resolved with a TLK file
457    pub strref: u32,
458    /// List of strings for different locales
459    pub strings: Vec<(i32, String)>,
460}
461
462/// GFF file format. Mutable counterpart of bin_gff::Gff
463#[derive(Debug, Serialize, Deserialize)]
464pub struct Gff {
465    /// GFF file type (BIC, UTC, ...)
466    pub file_type: FixedSizeString<4>,
467    /// GFF file version
468    pub file_version: FixedSizeString<4>,
469    /// GFF root `Struct`, where everything is stored.
470    pub root: Struct,
471}
472impl Gff {
473    /// Parse a GFF file from a byte array
474    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    /// Allocate a GFF tree from an existing read-only `bin_gff::Gff`
483    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                    // eprintln!("  BUILD FIELD {}", field_index);
495                    let gff_field = bingff.fields.get(*field_index as usize).unwrap();
496                    // eprintln!("    field: {:?}", gff_field);
497
498                    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!(), // TODO: remove Invalid ?
501                        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                            // eprintln!("    locstr: {:?}", gff_locstr);
535                            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    /// Serializes this GFF to human-readable text
607    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    /// Serializes this GFF into a read-only `bin_gff::Gff`
618    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            // Allocate field
646            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            // convert value
653            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                    // Note: total_len does not count the first u32
702                    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                    // list size
735                    bingff.list_indices[(list_offset / 4) as usize] = v.len() as u32;
736
737                    // struct indices
738                    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        // TODO: not great to generate a header struct twice
790        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    /// Serializes this GFF into a GFF file data
798    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        // no Char
820        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        // no DWord64
825        // no Int64
826        assert!((gff.root.find("ChallengeRating").unwrap().unwrap_float() - 100f32).abs() < 0.001);
827        // no Double
828        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        // no Void
845
846        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        // Tree navigation
854        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        // To GFF binary format
885        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        // No dword64
906        // No int64
907        assert_eq!(gff.root.find("XpMod").unwrap().unwrap_float(), 1f32);
908        // No double
909        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        // To GFF binary format
941        let mut bingff = gff.to_bin_gff();
942        // One struct has a non-zero data_or_data_offset with a field_count of 0
943        // The data_or_data_offset is meaningless and can be ignored
944        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        // no Char
956        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        // no DWord64
961        // no Int64
962        assert!((gff.root.find("ChallengeRating").unwrap().unwrap_float() - 11f32).abs() < 0.001);
963        // no Double
964        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        // no Void
975
976        let gfflist = gff.root.find("FeatList").unwrap().unwrap_list();
977        assert_eq!(gfflist.len(), 20);
978
979        // Tree navigation
980        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        // To GFF binary format
990        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        // To GFF binary format
999        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        // b.iter(test_gff_read_krogar);
1010    }
1011}