1use std::collections::HashMap;
4use std::io::{Cursor, Write};
5
6use rakata_core::{encode_text, text_encoding_for_language, TextEncoding};
7
8use super::{
9 binary::write_u32, to_u32, FieldType, Gff, GffBinaryError, GffValue, DEFAULT_TEXT_ENCODING,
10 FIELD_ENTRY_SIZE, GFF_HEADER_SIZE, GFF_VERSION_V32, LABEL_SIZE, STRUCT_ENTRY_SIZE,
11};
12
13#[cfg_attr(
15 feature = "tracing",
16 tracing::instrument(level = "debug", skip(writer, gff), fields(file_type = ?gff.file_type))
17)]
18pub fn write_gff<W: Write>(writer: &mut W, gff: &Gff) -> Result<(), GffBinaryError> {
19 let mut state = GffWriterState::default();
20 let root_index = state.build_struct(&gff.root)?;
21 if root_index != 0 {
22 return Err(GffBinaryError::InvalidData(
23 "internal writer error: root struct index is not zero".into(),
24 ));
25 }
26
27 let struct_offset = GFF_HEADER_SIZE;
28 let struct_table_size = state
29 .structs
30 .len()
31 .checked_mul(STRUCT_ENTRY_SIZE)
32 .ok_or(GffBinaryError::ValueOverflow("struct_table_size"))?;
33 let field_offset = struct_offset
34 .checked_add(struct_table_size)
35 .ok_or(GffBinaryError::ValueOverflow("field_offset"))?;
36 let field_table_size = state
37 .fields
38 .len()
39 .checked_mul(FIELD_ENTRY_SIZE)
40 .ok_or(GffBinaryError::ValueOverflow("field_table_size"))?;
41 let label_offset = field_offset
42 .checked_add(field_table_size)
43 .ok_or(GffBinaryError::ValueOverflow("label_offset"))?;
44 let labels_size = state
45 .labels
46 .len()
47 .checked_mul(LABEL_SIZE)
48 .ok_or(GffBinaryError::ValueOverflow("labels_size"))?;
49 let field_data_offset = label_offset
50 .checked_add(labels_size)
51 .ok_or(GffBinaryError::ValueOverflow("field_data_offset"))?;
52 let field_indices_offset = field_data_offset
53 .checked_add(state.field_data.len())
54 .ok_or(GffBinaryError::ValueOverflow("field_indices_offset"))?;
55 let list_indices_offset = field_indices_offset
56 .checked_add(state.field_indices.len())
57 .ok_or(GffBinaryError::ValueOverflow("list_indices_offset"))?;
58
59 writer.write_all(&gff.file_type)?;
60 writer.write_all(&GFF_VERSION_V32)?;
61 write_u32(writer, to_u32(struct_offset, "struct_offset")?)?;
62 write_u32(writer, to_u32(state.structs.len(), "struct_count")?)?;
63 write_u32(writer, to_u32(field_offset, "field_offset")?)?;
64 write_u32(writer, to_u32(state.fields.len(), "field_count")?)?;
65 write_u32(writer, to_u32(label_offset, "label_offset")?)?;
66 write_u32(writer, to_u32(state.labels.len(), "label_count")?)?;
67 write_u32(writer, to_u32(field_data_offset, "field_data_offset")?)?;
68 write_u32(writer, to_u32(state.field_data.len(), "field_data_count")?)?;
69 write_u32(
70 writer,
71 to_u32(field_indices_offset, "field_indices_offset")?,
72 )?;
73 write_u32(
74 writer,
75 to_u32(state.field_indices.len(), "field_indices_count")?,
76 )?;
77 write_u32(writer, to_u32(list_indices_offset, "list_indices_offset")?)?;
78 write_u32(
79 writer,
80 to_u32(state.list_indices.len(), "list_indices_count")?,
81 )?;
82
83 for entry in &state.structs {
84 write_u32(writer, u32::from_le_bytes(entry.struct_id.to_le_bytes()))?;
85 write_u32(writer, entry.data_or_offset)?;
86 write_u32(writer, entry.field_count)?;
87 }
88 for entry in &state.fields {
89 write_u32(writer, entry.field_type)?;
90 write_u32(writer, entry.label_index)?;
91 write_u32(writer, entry.data_or_offset)?;
92 }
93 for label in &state.labels {
94 let encoded =
95 encode_with_context(label, format!("label `{label}`"), DEFAULT_TEXT_ENCODING)?;
96 if encoded.len() > LABEL_SIZE {
97 return Err(GffBinaryError::LabelTooLong {
98 label: label.clone(),
99 len: encoded.len(),
100 max: LABEL_SIZE,
101 });
102 }
103 let mut field = [0_u8; LABEL_SIZE];
104 field[..encoded.len()].copy_from_slice(&encoded);
105 writer.write_all(&field)?;
106 }
107 writer.write_all(&state.field_data)?;
108 writer.write_all(&state.field_indices)?;
109 writer.write_all(&state.list_indices)?;
110 crate::trace_debug!(
111 struct_count = state.structs.len(),
112 field_count = state.fields.len(),
113 label_count = state.labels.len(),
114 field_data_len = state.field_data.len(),
115 field_indices_len = state.field_indices.len(),
116 list_indices_len = state.list_indices.len(),
117 "wrote gff tables to writer"
118 );
119 Ok(())
120}
121
122#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(gff)))]
124pub fn write_gff_to_vec(gff: &Gff) -> Result<Vec<u8>, GffBinaryError> {
125 let mut cursor = Cursor::new(Vec::new());
126 write_gff(&mut cursor, gff)?;
127 let bytes = cursor.into_inner();
128 crate::trace_debug!(bytes_len = bytes.len(), "serialized gff to vec");
129 Ok(bytes)
130}
131
132#[derive(Debug, Clone, Copy)]
133struct TempStructEntry {
134 struct_id: i32,
135 data_or_offset: u32,
136 field_count: u32,
137}
138
139#[derive(Debug, Clone, Copy)]
140struct TempFieldEntry {
141 field_type: u32,
142 label_index: u32,
143 data_or_offset: u32,
144}
145
146#[derive(Default)]
147struct GffWriterState {
148 labels: Vec<String>,
149 label_indices: HashMap<String, u32>,
150 structs: Vec<TempStructEntry>,
151 fields: Vec<TempFieldEntry>,
152 field_data: Vec<u8>,
153 field_indices: Vec<u8>,
154 list_indices: Vec<u8>,
155}
156
157impl GffWriterState {
158 fn build_struct(&mut self, structure: &super::GffStruct) -> Result<u32, GffBinaryError> {
159 let struct_index = self.structs.len();
160 let struct_index_u32 = to_u32(struct_index, "struct_index")?;
161 self.structs.push(TempStructEntry {
162 struct_id: structure.struct_id,
163 data_or_offset: u32::MAX,
164 field_count: 0,
165 });
166
167 let mut field_indices = Vec::with_capacity(structure.fields.len());
168 for field in &structure.fields {
169 field_indices.push(self.build_field(field)?);
170 }
171
172 let (data_or_offset, field_count) = match field_indices.len() {
173 0 => (u32::MAX, 0),
174 1 => (field_indices[0], 1),
175 _ => {
176 let offset = to_u32(self.field_indices.len(), "field_indices_offset")?;
177 for field_index in &field_indices {
178 push_u32(&mut self.field_indices, *field_index);
179 }
180 (offset, to_u32(field_indices.len(), "field_count")?)
181 }
182 };
183 self.structs[struct_index] = TempStructEntry {
184 struct_id: structure.struct_id,
185 data_or_offset,
186 field_count,
187 };
188 Ok(struct_index_u32)
189 }
190
191 fn build_field(&mut self, field: &super::GffField) -> Result<u32, GffBinaryError> {
192 let field_index = self.fields.len();
193 let field_index_u32 = to_u32(field_index, "field_index")?;
194 let label_index = self.intern_label(field.label.as_str())?;
195 self.fields.push(TempFieldEntry {
199 field_type: 0,
200 label_index,
201 data_or_offset: 0,
202 });
203 let (field_type, data_or_offset) = self.encode_field(&field.value)?;
204 self.fields[field_index] = TempFieldEntry {
205 field_type: u32::from(field_type),
206 label_index,
207 data_or_offset,
208 };
209 Ok(field_index_u32)
210 }
211
212 fn encode_field(&mut self, value: &GffValue) -> Result<(FieldType, u32), GffBinaryError> {
213 let payload = match value {
214 GffValue::UInt8(v) => (FieldType::UInt8, u32::from(*v)),
215 GffValue::Int8(v) => (
216 FieldType::Int8,
217 u32::from_le_bytes(i32::from(*v).to_le_bytes()),
218 ),
219 GffValue::UInt16(v) => (FieldType::UInt16, u32::from(*v)),
220 GffValue::Int16(v) => (
221 FieldType::Int16,
222 u32::from_le_bytes(i32::from(*v).to_le_bytes()),
223 ),
224 GffValue::UInt32(v) => (FieldType::UInt32, *v),
225 GffValue::Int32(v) => (FieldType::Int32, u32::from_le_bytes(v.to_le_bytes())),
226 GffValue::Single(v) => (FieldType::Single, v.to_bits()),
227 GffValue::StrRef(v) => (FieldType::StrRef, u32::from_le_bytes(v.raw().to_le_bytes())),
228 GffValue::UInt64(v) => {
229 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
230 self.field_data.extend_from_slice(&v.to_le_bytes());
231 (FieldType::UInt64, offset)
232 }
233 GffValue::Int64(v) => {
234 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
235 self.field_data.extend_from_slice(&v.to_le_bytes());
236 (FieldType::Int64, offset)
237 }
238 GffValue::Double(v) => {
239 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
240 self.field_data.extend_from_slice(&v.to_le_bytes());
241 (FieldType::Double, offset)
242 }
243 GffValue::String(v) => {
244 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
245 let encoded = encode_with_context(v, "string field".into(), DEFAULT_TEXT_ENCODING)?;
246 push_u32(
247 &mut self.field_data,
248 to_u32(encoded.len(), "string_length")?,
249 );
250 self.field_data.extend_from_slice(&encoded);
251 (FieldType::String, offset)
252 }
253 GffValue::ResRef(v) => {
254 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
255 let encoded =
256 encode_with_context(v.as_str(), "resref field".into(), DEFAULT_TEXT_ENCODING)?;
257 let len_u8 = u8::try_from(encoded.len())
258 .map_err(|_| GffBinaryError::ValueOverflow("resref_length"))?;
259 self.field_data.push(len_u8);
260 self.field_data.extend_from_slice(&encoded);
261 (FieldType::ResRef, offset)
262 }
263 GffValue::LocalizedString(v) => {
264 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
265 let mut payload = Vec::new();
266 push_u32(
267 &mut payload,
268 u32::from_le_bytes(v.string_ref.raw().to_le_bytes()),
269 );
270 push_u32(
271 &mut payload,
272 to_u32(v.substrings.len(), "locstring_substring_count")?,
273 );
274 for (substring_index, substring) in v.substrings.iter().enumerate() {
275 push_u32(&mut payload, substring.string_id);
276 let language_id = substring.string_id / 2;
277 let encoding = text_encoding_for_language(language_id)
278 .map_err(|err| {
282 GffBinaryError::UnsupportedLanguageEncoding(err.language_id.raw())
283 })?;
284 let encoded = encode_with_context(
285 &substring.text,
286 format!("locstring[{substring_index}] text"),
287 encoding,
288 )?;
289 push_u32(
290 &mut payload,
291 to_u32(encoded.len(), "locstring_substring_length")?,
292 );
293 payload.extend_from_slice(&encoded);
294 }
295 push_u32(
296 &mut self.field_data,
297 to_u32(payload.len(), "locstring_payload_size")?,
298 );
299 self.field_data.extend_from_slice(&payload);
300 (FieldType::LocalizedString, offset)
301 }
302 GffValue::Binary(v) => {
303 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
304 push_u32(&mut self.field_data, to_u32(v.len(), "binary_length")?);
305 self.field_data.extend_from_slice(v);
306 (FieldType::Binary, offset)
307 }
308 GffValue::Struct(v) => (FieldType::Struct, self.build_struct(v)?),
309 GffValue::List(v) => {
310 let offset = to_u32(self.list_indices.len(), "list_indices_offset")?;
311 push_u32(&mut self.list_indices, to_u32(v.len(), "list_count")?);
312 let indices_base = self.list_indices.len();
315 self.list_indices.resize(
316 indices_base
317 .checked_add(
318 v.len()
319 .checked_mul(4)
320 .ok_or(GffBinaryError::ValueOverflow("list_indices_size"))?,
321 )
322 .ok_or(GffBinaryError::ValueOverflow("list_indices_size"))?,
323 0,
324 );
325 for (index, item) in v.iter().enumerate() {
326 let struct_index = self.build_struct(item)?;
327 let slot = indices_base
328 .checked_add(
329 index
330 .checked_mul(4)
331 .ok_or(GffBinaryError::ValueOverflow("list_index_slot"))?,
332 )
333 .ok_or(GffBinaryError::ValueOverflow("list_index_slot"))?;
334 self.list_indices[slot..slot + 4].copy_from_slice(&struct_index.to_le_bytes());
335 }
336 (FieldType::List, offset)
337 }
338 GffValue::Vector4(v) => {
339 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
340 for value in v {
341 self.field_data.extend_from_slice(&value.to_le_bytes());
342 }
343 (FieldType::Vector4, offset)
344 }
345 GffValue::Vector3(v) => {
346 let offset = to_u32(self.field_data.len(), "field_data_offset")?;
347 for value in v {
348 self.field_data.extend_from_slice(&value.to_le_bytes());
349 }
350 (FieldType::Vector3, offset)
351 }
352 };
353 Ok(payload)
354 }
355
356 fn intern_label(&mut self, label: &str) -> Result<u32, GffBinaryError> {
357 if let Some(existing) = self.label_indices.get(label) {
358 return Ok(*existing);
359 }
360 let encoded =
361 encode_with_context(label, format!("label `{label}`"), DEFAULT_TEXT_ENCODING)?;
362 if encoded.len() > LABEL_SIZE {
363 return Err(GffBinaryError::LabelTooLong {
364 label: label.to_string(),
365 len: encoded.len(),
366 max: LABEL_SIZE,
367 });
368 }
369 let index = to_u32(self.labels.len(), "label_index")?;
370 self.labels.push(label.to_string());
371 self.label_indices.insert(label.to_string(), index);
372 Ok(index)
373 }
374}
375
376fn encode_with_context(
377 text: &str,
378 context: String,
379 encoding: TextEncoding,
380) -> Result<Vec<u8>, GffBinaryError> {
381 encode_text(text, encoding).map_err(|source| GffBinaryError::TextEncoding { context, source })
382}
383
384fn push_u32(bytes: &mut Vec<u8>, value: u32) {
385 bytes.extend_from_slice(&value.to_le_bytes());
386}