rakata_formats/rim/
writer.rs

1//! RIM binary writer.
2
3use std::io::{Cursor, Write};
4
5use crate::binary::write_u32;
6
7use super::{Rim, RimBinaryError, FILE_HEADER_SIZE, KEY_ENTRY_SIZE, RIM_MAGIC, RIM_VERSION_V10};
8
9/// Writes a RIM archive to a writer.
10#[cfg_attr(
11    feature = "tracing",
12    tracing::instrument(level = "debug", skip(writer, rim))
13)]
14pub fn write_rim<W: Write>(writer: &mut W, rim: &Rim) -> Result<(), RimBinaryError> {
15    let entry_count = u32::try_from(rim.resources.len())
16        .map_err(|_| RimBinaryError::ValueOverflow("entry_count"))?;
17    let keys_offset = u32::try_from(FILE_HEADER_SIZE)
18        .map_err(|_| RimBinaryError::ValueOverflow("keys_offset"))?;
19    let mut next_data_offset = keys_offset
20        .checked_add(
21            entry_count
22                .checked_mul(
23                    u32::try_from(KEY_ENTRY_SIZE)
24                        .map_err(|_| RimBinaryError::ValueOverflow("KEY_ENTRY_SIZE"))?,
25                )
26                .ok_or(RimBinaryError::ValueOverflow("data_offset"))?,
27        )
28        .ok_or(RimBinaryError::ValueOverflow("data_offset"))?;
29
30    writer.write_all(&RIM_MAGIC)?;
31    writer.write_all(&RIM_VERSION_V10)?;
32    write_u32(writer, rim.reserved_0x08)?;
33    write_u32(writer, entry_count)?;
34    write_u32(writer, keys_offset)?;
35    write_u32(writer, rim.reserved_0x14)?;
36    writer.write_all(&rim.reserved_0x18)?;
37
38    for (index, resource) in rim.resources.iter().enumerate() {
39        // ResRef guarantees ASCII-only content and length <= 16, so we can
40        // write the bytes directly without encoding or validation.
41        let resref_bytes = resource.resref.as_str().as_bytes();
42        let mut key_resref = [0_u8; 16];
43        key_resref[..resref_bytes.len()].copy_from_slice(resref_bytes);
44        writer.write_all(&key_resref)?;
45        write_u32(writer, u32::from(resource.resource_type.raw_id()))?;
46        write_u32(
47            writer,
48            u32::try_from(index).map_err(|_| RimBinaryError::ValueOverflow("resource_id"))?,
49        )?;
50        write_u32(writer, next_data_offset)?;
51        let data_len = u32::try_from(resource.data.len())
52            .map_err(|_| RimBinaryError::ValueOverflow("resource_size"))?;
53        write_u32(writer, data_len)?;
54        next_data_offset = next_data_offset
55            .checked_add(data_len)
56            .ok_or(RimBinaryError::ValueOverflow("data_offset"))?;
57    }
58
59    for resource in &rim.resources {
60        writer.write_all(&resource.data)?;
61    }
62
63    crate::trace_debug!(resource_count = rim.resources.len(), "wrote rim to writer");
64    Ok(())
65}
66
67/// Serializes a RIM archive to bytes.
68#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(rim)))]
69pub fn write_rim_to_vec(rim: &Rim) -> Result<Vec<u8>, RimBinaryError> {
70    let mut cursor = Cursor::new(Vec::new());
71    write_rim(&mut cursor, rim)?;
72    let bytes = cursor.into_inner();
73    crate::trace_debug!(bytes_len = bytes.len(), "serialized rim to vec");
74    Ok(bytes)
75}