1use std::{
2 collections::BTreeMap,
3 io::{Seek, SeekFrom, Write},
4};
5
6use indexmap::IndexMap;
7
8use crate::{Endian, Offset, Xc3Result, Xc3Write};
9
10#[derive(Debug)]
13pub struct WriteOptions {
14 pub start_alignment: u64,
16
17 pub start_padding_byte: u8,
19
20 pub string_alignment: u64,
22
23 pub string_padding_byte: u8,
25}
26
27impl Default for WriteOptions {
28 fn default() -> Self {
29 Self {
30 start_alignment: 1,
31 start_padding_byte: 0,
32 string_alignment: 1,
33 string_padding_byte: 0,
34 }
35 }
36}
37
38#[derive(Debug, Clone, Default)]
40pub struct StringSectionUniqueSorted {
41 name_to_offsets: BTreeMap<String, Vec<u64>>,
43}
44
45impl StringSectionUniqueSorted {
46 pub fn insert_offset64(&mut self, offset: &Offset<'_, u64, String>) {
48 self.name_to_offsets
49 .entry(offset.data.clone())
50 .or_default()
51 .push(offset.position);
52 }
53
54 pub fn write<W: Write + Seek>(
56 &self,
57 writer: &mut W,
58 data_ptr: &mut u64,
59 options: &WriteOptions,
60 endian: Endian,
61 ) -> Xc3Result<()> {
62 let name_positions = write_strings(self.name_to_offsets.keys(), writer, data_ptr, options)?;
63
64 for (offsets, position) in self.name_to_offsets.values().zip(name_positions) {
65 update_offsets(writer, 0, endian, position as u64, offsets)?;
66 }
67
68 Ok(())
69 }
70}
71
72#[derive(Default)]
74pub struct StringSectionUnique {
75 name_to_offsets: IndexMap<String, Vec<u64>>,
76}
77
78impl StringSectionUnique {
79 pub fn insert_offset32(&mut self, offset: &Offset<'_, u32, String>) {
81 self.name_to_offsets
82 .entry(offset.data.clone())
83 .or_default()
84 .push(offset.position);
85 }
86
87 pub fn write<W: Write + Seek>(
89 &self,
90 writer: &mut W,
91 base_offset: u64,
92 data_ptr: &mut u64,
93 options: &WriteOptions,
94 endian: Endian,
95 ) -> Xc3Result<()> {
96 let name_positions = write_strings(self.name_to_offsets.keys(), writer, data_ptr, options)?;
97
98 for (offsets, position) in self.name_to_offsets.values().zip(name_positions) {
99 update_offsets(writer, base_offset, endian, position as u64, offsets)?;
100 }
101
102 Ok(())
103 }
104}
105
106#[derive(Default)]
108pub struct StringSection {
109 name_to_offset: Vec<(String, u64)>,
110}
111
112impl StringSection {
113 pub fn insert_offset32(&mut self, offset: &Offset<'_, u32, String>) {
115 self.name_to_offset
116 .push((offset.data.clone(), offset.position));
117 }
118
119 pub fn write<W: std::io::Write + std::io::Seek>(
121 &self,
122 writer: &mut W,
123 base_offset: u64,
124 data_ptr: &mut u64,
125 options: &WriteOptions,
126 endian: Endian,
127 ) -> Xc3Result<()> {
128 let name_positions = write_strings(
129 self.name_to_offset.iter().map(|(n, _)| n),
130 writer,
131 data_ptr,
132 options,
133 )?;
134
135 for ((_, offset), position) in self.name_to_offset.iter().zip(name_positions) {
137 update_offsets(writer, base_offset, endian, position as u64, &[*offset])?;
138 }
139
140 Ok(())
141 }
142}
143
144fn update_offsets<W: Write + Seek>(
145 writer: &mut W,
146 base_offset: u64,
147 endian: Endian,
148 position: u64,
149 offsets: &[u64],
150) -> Result<(), std::io::Error> {
151 for offset in offsets {
152 writer.seek(SeekFrom::Start(*offset))?;
154 let final_offset = position - base_offset;
155 (final_offset as u32).xc3_write(writer, endian)?;
156 }
157 Ok(())
158}
159
160fn write_strings<'a, W: Write + Seek>(
161 names: impl Iterator<Item = &'a String>,
162 writer: &mut W,
163 data_ptr: &mut u64,
164 options: &WriteOptions,
165) -> Xc3Result<Vec<u32>> {
166 let mut name_positions = Vec::new();
167 writer.seek(std::io::SeekFrom::Start(*data_ptr))?;
168 align(
169 writer,
170 *data_ptr,
171 options.start_alignment,
172 options.start_padding_byte,
173 )?;
174
175 for name in names {
176 let position = writer.stream_position()? as u32;
178
179 writer.write_all(name.as_bytes())?;
180 writer.write_all(&[0u8])?;
181
182 let position_after_write = writer.stream_position()?;
184 align(
185 writer,
186 position_after_write,
187 options.string_alignment,
188 options.string_padding_byte,
189 )?;
190
191 name_positions.push(position);
192 }
193 *data_ptr = (*data_ptr).max(writer.stream_position()?);
194
195 Ok(name_positions)
196}
197
198fn align<W: Write>(writer: &mut W, size: u64, align: u64, pad: u8) -> std::io::Result<()> {
199 let aligned_size = size.next_multiple_of(align);
200 let padding = aligned_size - size;
201 writer.write_all(&vec![pad; padding as usize])?;
202 Ok(())
203}