1use std::io::{Cursor, Write};
4
5use rakata_core::encode_text;
6
7use super::{
8 binary::{write_u16, write_u32},
9 Erf, ErfBinaryError, ErfFileType, ErfWriteMode, ErfWriteOptions, ModLayout, ERF_TEXT_ENCODING,
10 ERF_VERSION_V10, FILE_HEADER_SIZE, KEY_ENTRY_SIZE, MOD_BLANK_BLOCK_ENTRY_SIZE,
11 RESOURCE_ENTRY_SIZE,
12};
13
14#[cfg_attr(
21 feature = "tracing",
22 tracing::instrument(level = "debug", skip(writer, erf))
23)]
24pub fn write_erf<W: Write>(writer: &mut W, erf: &Erf) -> Result<(), ErfBinaryError> {
25 write_erf_with_options(writer, erf, ErfWriteOptions::default())
26}
27
28#[cfg_attr(
43 feature = "tracing",
44 tracing::instrument(level = "debug", skip(writer, erf), fields(file_type = ?erf.file_type, mod_layout = ?options.mod_layout))
45)]
46pub fn write_erf_with_options<W: Write>(
47 writer: &mut W,
48 erf: &Erf,
49 options: ErfWriteOptions,
50) -> Result<(), ErfBinaryError> {
51 let entry_count = u32::try_from(erf.resources.len())
52 .map_err(|_| ErfBinaryError::ValueOverflow("entry_count"))?;
53
54 let mut localized_bytes = Vec::new();
55 for (index, entry) in erf.localized_strings.iter().enumerate() {
56 push_u32(&mut localized_bytes, entry.language_id.raw());
57 let encoded = encode_text(&entry.text, ERF_TEXT_ENCODING).map_err(|source| {
58 ErfBinaryError::TextEncoding {
59 context: format!("localized_strings[{index}]"),
60 source,
61 }
62 })?;
63 let encoded_len = u32::try_from(encoded.len())
64 .map_err(|_| ErfBinaryError::ValueOverflow("localized_string_len"))?;
65 push_u32(&mut localized_bytes, encoded_len);
66 localized_bytes.extend_from_slice(&encoded);
67 }
68 let language_count = u32::try_from(erf.localized_strings.len())
69 .map_err(|_| ErfBinaryError::ValueOverflow("language_count"))?;
70 let localized_size = u32::try_from(localized_bytes.len())
71 .map_err(|_| ErfBinaryError::ValueOverflow("localized_string_size"))?;
72
73 let localized_offset = u32::try_from(FILE_HEADER_SIZE)
74 .map_err(|_| ErfBinaryError::ValueOverflow("localized_offset"))?;
75 let keys_offset = localized_offset
76 .checked_add(localized_size)
77 .ok_or(ErfBinaryError::ValueOverflow("keys_offset"))?;
78 let mod_blank_block_size = if erf.file_type == ErfFileType::Mod
79 && options.mod_layout == ModLayout::WithBlankBlock
80 {
81 entry_count
82 .checked_mul(u32::try_from(MOD_BLANK_BLOCK_ENTRY_SIZE).expect("constant fits in u32"))
83 .ok_or(ErfBinaryError::ValueOverflow("mod_blank_block_size"))?
84 } else {
85 0
86 };
87 let resources_offset = keys_offset
88 .checked_add(
89 entry_count
90 .checked_mul(u32::try_from(KEY_ENTRY_SIZE).expect("constant fits in u32"))
91 .ok_or(ErfBinaryError::ValueOverflow("resources_offset"))?,
92 )
93 .and_then(|offset| offset.checked_add(mod_blank_block_size))
94 .ok_or(ErfBinaryError::ValueOverflow("resources_offset"))?;
95
96 let serialized_file_type = match options.output {
97 ErfWriteMode::CanonicalK1 if erf.file_type == ErfFileType::Sav => ErfFileType::Mod,
98 _ => erf.file_type,
99 };
100
101 writer.write_all(&serialized_file_type.fourcc())?;
102 writer.write_all(&ERF_VERSION_V10)?;
103 write_u32(writer, language_count)?;
104 write_u32(writer, localized_size)?;
105 write_u32(writer, entry_count)?;
106 write_u32(writer, localized_offset)?;
107 write_u32(writer, keys_offset)?;
108 write_u32(writer, resources_offset)?;
109 write_u32(writer, erf.build_year)?;
110 write_u32(writer, erf.build_day)?;
111 write_u32(
112 writer,
113 u32::from_le_bytes(erf.description_strref.raw().to_le_bytes()),
114 )?;
115 writer.write_all(&erf.reserved)?;
116
117 writer.write_all(&localized_bytes)?;
118
119 for (index, resource) in erf.resources.iter().enumerate() {
120 let key = resource
127 .name_as_read
128 .unwrap_or_else(|| resource.resref.to_padded());
129 writer.write_all(&key)?;
130 write_u32(
131 writer,
132 u32::try_from(index).map_err(|_| ErfBinaryError::ValueOverflow("resource_id"))?,
133 )?;
134 write_u16(writer, resource.resource_type.raw_id())?;
135 write_u16(writer, 0)?;
136 }
137
138 if mod_blank_block_size > 0 {
139 writer.write_all(&vec![
140 0_u8;
141 usize::try_from(mod_blank_block_size).map_err(
142 |_| { ErfBinaryError::ValueOverflow("mod_blank_block_size") }
143 )?
144 ])?;
145 }
146
147 let mut next_data_offset = resources_offset
148 .checked_add(
149 entry_count
150 .checked_mul(u32::try_from(RESOURCE_ENTRY_SIZE).expect("constant fits in u32"))
151 .ok_or(ErfBinaryError::ValueOverflow("data_offset"))?,
152 )
153 .ok_or(ErfBinaryError::ValueOverflow("data_offset"))?;
154 for resource in &erf.resources {
155 write_u32(writer, next_data_offset)?;
156 let data_len = u32::try_from(resource.data.len())
157 .map_err(|_| ErfBinaryError::ValueOverflow("resource_size"))?;
158 write_u32(writer, data_len)?;
159 next_data_offset = next_data_offset
160 .checked_add(data_len)
161 .ok_or(ErfBinaryError::ValueOverflow("data_offset"))?;
162 }
163
164 for resource in &erf.resources {
165 writer.write_all(&resource.data)?;
166 }
167 crate::trace_debug!(
168 file_type = ?erf.file_type,
169 localized_string_count = erf.localized_strings.len(),
170 resource_count = erf.resources.len(),
171 "wrote erf-family archive to writer"
172 );
173 Ok(())
174}
175
176#[cfg_attr(
185 feature = "tracing",
186 tracing::instrument(level = "debug", skip(writer, erf))
187)]
188pub fn write_save_archive<W: Write>(writer: &mut W, erf: &Erf) -> Result<(), ErfBinaryError> {
189 let mut archive = erf.clone();
190 archive.file_type = ErfFileType::Mod;
191 write_erf(writer, &archive)
192}
193
194#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(erf)))]
200pub fn write_erf_to_vec(erf: &Erf) -> Result<Vec<u8>, ErfBinaryError> {
201 write_erf_to_vec_with_options(erf, ErfWriteOptions::default())
202}
203
204#[cfg_attr(
211 feature = "tracing",
212 tracing::instrument(level = "debug", skip(erf), fields(file_type = ?erf.file_type, mod_layout = ?options.mod_layout))
213)]
214pub fn write_erf_to_vec_with_options(
215 erf: &Erf,
216 options: ErfWriteOptions,
217) -> Result<Vec<u8>, ErfBinaryError> {
218 let mut cursor = Cursor::new(Vec::new());
219 write_erf_with_options(&mut cursor, erf, options)?;
220 let bytes = cursor.into_inner();
221 crate::trace_debug!(
222 bytes_len = bytes.len(),
223 "serialized erf-family archive to vec"
224 );
225 Ok(bytes)
226}
227
228#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(erf)))]
237pub fn write_save_archive_to_vec(erf: &Erf) -> Result<Vec<u8>, ErfBinaryError> {
238 let mut cursor = Cursor::new(Vec::new());
239 write_save_archive(&mut cursor, erf)?;
240 let bytes = cursor.into_inner();
241 crate::trace_debug!(bytes_len = bytes.len(), "serialized save archive to vec");
242 Ok(bytes)
243}
244
245fn push_u32(bytes: &mut Vec<u8>, value: u32) {
246 bytes.extend_from_slice(&value.to_le_bytes());
247}