rakata_formats/tlk/
writer.rs1use std::io::{Cursor, Write};
4
5use rakata_core::{encode_text, text_encoding_for_language, ResRef};
6
7use crate::binary::{write_f32, write_u32};
8
9use super::{Tlk, TlkBinaryError, ENTRY_SIZE, FILE_HEADER_SIZE, TLK_MAGIC, TLK_VERSION_V3};
10
11#[cfg_attr(
23 feature = "tracing",
24 tracing::instrument(level = "debug", skip(writer, tlk))
25)]
26pub fn write_tlk<W: Write>(writer: &mut W, tlk: &Tlk) -> Result<(), TlkBinaryError> {
27 let text_encoding = text_encoding_for_language(tlk.language_id)
28 .map_err(|err| TlkBinaryError::UnsupportedLanguageEncoding(err.language_id.raw()))?;
29 let entry_count = u32::try_from(tlk.entries.len())
30 .map_err(|_| TlkBinaryError::ValueOverflow("entry_count"))?;
31 let entries_offset = u32::try_from(
32 FILE_HEADER_SIZE
33 .checked_add(
34 tlk.entries
35 .len()
36 .checked_mul(ENTRY_SIZE)
37 .ok_or(TlkBinaryError::ValueOverflow("entries_offset"))?,
38 )
39 .ok_or(TlkBinaryError::ValueOverflow("entries_offset"))?,
40 )
41 .map_err(|_| TlkBinaryError::ValueOverflow("entries_offset"))?;
42
43 writer.write_all(&TLK_MAGIC)?;
44 writer.write_all(&TLK_VERSION_V3)?;
45 write_u32(writer, tlk.language_id.raw())?;
46 write_u32(writer, entry_count)?;
47 write_u32(writer, entries_offset)?;
48
49 let mut text_blob = Vec::new();
50 for (entry_index, entry) in tlk.entries.iter().enumerate() {
51 let normalized = entry.normalized();
52 let text_bytes = encode_text(&normalized.text, text_encoding).map_err(|source| {
53 TlkBinaryError::TextEncoding {
54 entry_index,
55 source,
56 }
57 })?;
58 let text_offset = u32::try_from(text_blob.len())
59 .map_err(|_| TlkBinaryError::ValueOverflow("text_offset"))?;
60 let text_length = u32::try_from(text_bytes.len())
61 .map_err(|_| TlkBinaryError::ValueOverflow("text_length"))?;
62
63 let mut flags = 0_u32;
64 if normalized.text_present {
65 flags |= 0x0001;
66 }
67 if normalized.sound_present {
68 flags |= 0x0002;
69 }
70 if normalized.sound_length_present {
71 flags |= 0x0004;
72 }
73
74 write_u32(writer, flags)?;
75 write_resref_field(writer, &normalized.voiceover)?;
76 write_u32(writer, normalized.volume_var)?;
77 write_u32(writer, normalized.pitch_var)?;
78 write_u32(writer, text_offset)?;
79 write_u32(writer, text_length)?;
80 write_f32(writer, normalized.sound_length)?;
81
82 text_blob.extend_from_slice(&text_bytes);
83 }
84
85 writer.write_all(&text_blob)?;
86 Ok(())
87}
88
89#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(tlk)))]
96pub fn write_tlk_to_vec(tlk: &Tlk) -> Result<Vec<u8>, TlkBinaryError> {
97 let mut cursor = Cursor::new(Vec::new());
98 write_tlk(&mut cursor, tlk)?;
99 Ok(cursor.into_inner())
100}
101
102fn write_resref_field<W: Write>(writer: &mut W, resref: &ResRef) -> Result<(), TlkBinaryError> {
104 writer.write_all(&resref.to_padded())?;
107 Ok(())
108}