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