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(
13 feature = "tracing",
14 tracing::instrument(level = "debug", skip(writer, tlk))
15)]
16pub fn write_tlk<W: Write>(writer: &mut W, tlk: &Tlk) -> Result<(), TlkBinaryError> {
17 let text_encoding = text_encoding_for_language(tlk.language_id)
18 .map_err(|err| TlkBinaryError::UnsupportedLanguageEncoding(err.language_id.raw()))?;
19 let entry_count = u32::try_from(tlk.entries.len())
20 .map_err(|_| TlkBinaryError::ValueOverflow("entry_count"))?;
21 let entries_offset = u32::try_from(
22 FILE_HEADER_SIZE
23 .checked_add(
24 tlk.entries
25 .len()
26 .checked_mul(ENTRY_SIZE)
27 .ok_or(TlkBinaryError::ValueOverflow("entries_offset"))?,
28 )
29 .ok_or(TlkBinaryError::ValueOverflow("entries_offset"))?,
30 )
31 .map_err(|_| TlkBinaryError::ValueOverflow("entries_offset"))?;
32
33 writer.write_all(&TLK_MAGIC)?;
34 writer.write_all(&TLK_VERSION_V3)?;
35 write_u32(writer, tlk.language_id.raw())?;
36 write_u32(writer, entry_count)?;
37 write_u32(writer, entries_offset)?;
38
39 let mut text_blob = Vec::new();
40 for (entry_index, entry) in tlk.entries.iter().enumerate() {
41 let normalized = entry.normalized();
42 let text_bytes = encode_text(&normalized.text, text_encoding).map_err(|source| {
43 TlkBinaryError::TextEncoding {
44 entry_index,
45 source,
46 }
47 })?;
48 let text_offset = u32::try_from(text_blob.len())
49 .map_err(|_| TlkBinaryError::ValueOverflow("text_offset"))?;
50 let text_length = u32::try_from(text_bytes.len())
51 .map_err(|_| TlkBinaryError::ValueOverflow("text_length"))?;
52
53 let mut flags = 0_u32;
54 if normalized.text_present {
55 flags |= 0x0001;
56 }
57 if normalized.sound_present {
58 flags |= 0x0002;
59 }
60 if normalized.sound_length_present {
61 flags |= 0x0004;
62 }
63
64 write_u32(writer, flags)?;
65 write_resref_field(writer, &normalized.voiceover)?;
66 write_u32(writer, normalized.volume_var)?;
67 write_u32(writer, normalized.pitch_var)?;
68 write_u32(writer, text_offset)?;
69 write_u32(writer, text_length)?;
70 write_f32(writer, normalized.sound_length)?;
71
72 text_blob.extend_from_slice(&text_bytes);
73 }
74
75 writer.write_all(&text_blob)?;
76 Ok(())
77}
78
79#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(tlk)))]
81pub fn write_tlk_to_vec(tlk: &Tlk) -> Result<Vec<u8>, TlkBinaryError> {
82 let mut cursor = Cursor::new(Vec::new());
83 write_tlk(&mut cursor, tlk)?;
84 Ok(cursor.into_inner())
85}
86
87fn write_resref_field<W: Write>(writer: &mut W, resref: &ResRef) -> Result<(), TlkBinaryError> {
89 let mut field = [0_u8; 16];
90 let bytes = resref.as_str().as_bytes();
91 if bytes.len() > field.len() {
92 return Err(TlkBinaryError::ValueOverflow("sound_resref"));
93 }
94 field[..bytes.len()].copy_from_slice(bytes);
95 writer.write_all(&field)?;
96 Ok(())
97}