Skip to main content

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 stores Windows-1252 bytes (the engine's native
40        // encoding) capped at 16 bytes, which is exactly the on-disk
41        // shape, so we write `as_bytes()` directly with no transcoding.
42        let resref_bytes = resource.resref.as_bytes();
43        let mut key_resref = [0_u8; 16];
44        key_resref[..resref_bytes.len()].copy_from_slice(resref_bytes);
45        writer.write_all(&key_resref)?;
46        write_u32(writer, u32::from(resource.resource_type.raw_id()))?;
47        write_u32(
48            writer,
49            u32::try_from(index).map_err(|_| RimBinaryError::ValueOverflow("resource_id"))?,
50        )?;
51        write_u32(writer, next_data_offset)?;
52        let data_len = u32::try_from(resource.data.len())
53            .map_err(|_| RimBinaryError::ValueOverflow("resource_size"))?;
54        write_u32(writer, data_len)?;
55        next_data_offset = next_data_offset
56            .checked_add(data_len)
57            .ok_or(RimBinaryError::ValueOverflow("data_offset"))?;
58    }
59
60    for resource in &rim.resources {
61        writer.write_all(&resource.data)?;
62    }
63
64    crate::trace_debug!(resource_count = rim.resources.len(), "wrote rim to writer");
65    Ok(())
66}
67
68/// Serializes a RIM archive to bytes.
69#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", skip(rim)))]
70pub fn write_rim_to_vec(rim: &Rim) -> Result<Vec<u8>, RimBinaryError> {
71    let mut cursor = Cursor::new(Vec::new());
72    write_rim(&mut cursor, rim)?;
73    let bytes = cursor.into_inner();
74    crate::trace_debug!(bytes_len = bytes.len(), "serialized rim to vec");
75    Ok(bytes)
76}