rakata_formats/key/
writer.rs1use std::io::{Cursor, Write};
4
5use rakata_core::encode_text;
6
7use super::{
8 binary::{write_u16, write_u32},
9 Key, KeyBinaryError, FILE_ENTRY_SIZE, FILE_HEADER_SIZE, KEY_MAGIC, KEY_TEXT_ENCODING,
10 KEY_VERSION_V10,
11};
12
13#[cfg_attr(
28 feature = "tracing",
29 tracing::instrument(level = "debug", skip(writer, key))
30)]
31pub fn write_key<W: Write>(writer: &mut W, key: &Key) -> Result<(), KeyBinaryError> {
32 let bif_count = u32::try_from(key.bif_entries.len())
33 .map_err(|_| KeyBinaryError::ValueOverflow("bif_count"))?;
34 let key_count = u32::try_from(key.resources.len())
35 .map_err(|_| KeyBinaryError::ValueOverflow("key_count"))?;
36
37 let file_table_offset = u32::try_from(FILE_HEADER_SIZE)
38 .map_err(|_| KeyBinaryError::ValueOverflow("file_table_offset"))?;
39 let file_table_size = key
40 .bif_entries
41 .len()
42 .checked_mul(FILE_ENTRY_SIZE)
43 .ok_or(KeyBinaryError::ValueOverflow("file_table_size"))?;
44
45 let mut encoded_filenames = Vec::with_capacity(key.bif_entries.len());
46 let mut filenames_table_size = 0usize;
47 for (index, bif_entry) in key.bif_entries.iter().enumerate() {
48 if bif_entry.filename.contains('\0') {
49 return Err(KeyBinaryError::FilenameContainsNul {
50 filename: bif_entry.filename.clone(),
51 });
52 }
53 let encoded = encode_text(&bif_entry.filename, KEY_TEXT_ENCODING).map_err(|source| {
54 KeyBinaryError::TextEncoding {
55 context: format!("bif_entries[{index}].filename"),
56 source,
57 }
58 })?;
59
60 let disk_len = encoded
61 .len()
62 .checked_add(1)
63 .ok_or(KeyBinaryError::ValueOverflow("filename_size"))?;
64 if disk_len > usize::from(u16::MAX) {
65 return Err(KeyBinaryError::FilenameTooLong {
66 filename: bif_entry.filename.clone(),
67 len: disk_len,
68 max: usize::from(u16::MAX),
69 });
70 }
71
72 filenames_table_size = filenames_table_size
73 .checked_add(disk_len)
74 .ok_or(KeyBinaryError::ValueOverflow("filenames_table_size"))?;
75 encoded_filenames.push(encoded);
76 }
77
78 let key_table_offset_usize = FILE_HEADER_SIZE
79 .checked_add(file_table_size)
80 .and_then(|offset| offset.checked_add(filenames_table_size))
81 .ok_or(KeyBinaryError::ValueOverflow("key_table_offset"))?;
82 let key_table_offset = u32::try_from(key_table_offset_usize)
83 .map_err(|_| KeyBinaryError::ValueOverflow("key_table_offset"))?;
84
85 writer.write_all(&KEY_MAGIC)?;
86 writer.write_all(&KEY_VERSION_V10)?;
87 write_u32(writer, bif_count)?;
88 write_u32(writer, key_count)?;
89 write_u32(writer, file_table_offset)?;
90 write_u32(writer, key_table_offset)?;
91 write_u32(writer, key.build_year)?;
92 write_u32(writer, key.build_day)?;
93 writer.write_all(&key.reserved)?;
94
95 let mut next_filename_offset = file_table_offset
96 .checked_add(
97 u32::try_from(file_table_size)
98 .map_err(|_| KeyBinaryError::ValueOverflow("file_table_size"))?,
99 )
100 .ok_or(KeyBinaryError::ValueOverflow("filename_offset"))?;
101
102 for (bif_entry, encoded_filename) in key.bif_entries.iter().zip(encoded_filenames.iter()) {
103 let disk_len = encoded_filename
104 .len()
105 .checked_add(1)
106 .ok_or(KeyBinaryError::ValueOverflow("filename_size"))?;
107 write_u32(writer, bif_entry.file_size)?;
108 write_u32(writer, next_filename_offset)?;
109 write_u16(
110 writer,
111 u16::try_from(disk_len).map_err(|_| KeyBinaryError::ValueOverflow("filename_size"))?,
112 )?;
113 write_u16(writer, bif_entry.drives)?;
114
115 next_filename_offset = next_filename_offset
116 .checked_add(
117 u32::try_from(disk_len)
118 .map_err(|_| KeyBinaryError::ValueOverflow("filename_size"))?,
119 )
120 .ok_or(KeyBinaryError::ValueOverflow("filename_offset"))?;
121 }
122
123 for encoded_filename in &encoded_filenames {
124 writer.write_all(encoded_filename)?;
125 writer.write_all(&[0_u8])?;
126 }
127
128 for resource in &key.resources {
129 writer.write_all(&resource.resref.to_padded())?;
132 write_u16(writer, resource.resource_type.raw_id())?;
133 write_u32(writer, resource.resource_id.raw())?;
134 }
135
136 crate::trace_debug!(
137 bif_count = key.bif_entries.len(),
138 resource_count = key.resources.len(),
139 "wrote key to writer"
140 );
141 Ok(())
142}
143
144#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(key)))]
151pub fn write_key_to_vec(key: &Key) -> Result<Vec<u8>, KeyBinaryError> {
152 let mut cursor = Cursor::new(Vec::new());
153 write_key(&mut cursor, key)?;
154 let bytes = cursor.into_inner();
155 crate::trace_debug!(bytes_len = bytes.len(), "serialized key to vec");
156 Ok(bytes)
157}