1use std::io::Seek;
3use std::io::SeekFrom;
4use std::io::Write;
5use std::marker::PhantomData;
6use std::ops::Deref;
7
8pub type Xc3Result<T> = Result<T, std::io::Error>;
11
12pub mod strings;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Endian {
16 Little,
17 Big,
18}
19
20pub trait Xc3Write {
22 type Offsets<'a>
24 where
25 Self: 'a;
26
27 fn xc3_write<W: Write + Seek>(
35 &self,
36 writer: &mut W,
37 endian: Endian,
38 ) -> Xc3Result<Self::Offsets<'_>>;
39
40 fn should_write(&self) -> Option<bool> {
44 Some(true)
45 }
46
47 const ALIGNMENT: u64 = 4;
49}
50
51pub trait Xc3WriteOffsets {
53 type Args;
54
55 fn write_offsets<W: Write + Seek>(
63 &self,
64 writer: &mut W,
65 base_offset: u64,
66 data_ptr: &mut u64,
67 endian: Endian,
68 args: Self::Args,
69 ) -> Xc3Result<()>;
70}
71
72pub trait WriteFull {
77 type Args;
79
80 fn write_full<W: Write + Seek>(
85 &self,
86 writer: &mut W,
87 base_offset: u64,
88 data_ptr: &mut u64,
89 endian: Endian,
90 offset_args: Self::Args,
91 ) -> Xc3Result<()>;
92}
93
94impl<T, A> WriteFull for T
95where
96 T: Xc3Write,
97 for<'a> T::Offsets<'a>: Xc3WriteOffsets<Args = A>,
98{
99 type Args = A;
100
101 fn write_full<W: Write + Seek>(
102 &self,
103 writer: &mut W,
104 base_offset: u64,
105 data_ptr: &mut u64,
106 endian: Endian,
107 offset_args: Self::Args,
108 ) -> Xc3Result<()> {
109 let offsets = self.xc3_write(writer, endian)?;
111 *data_ptr = (*data_ptr).max(writer.stream_position()?);
112
113 offsets.write_offsets(writer, base_offset, data_ptr, endian, offset_args)?;
114 *data_ptr = (*data_ptr).max(writer.stream_position()?);
116 Ok(())
117 }
118}
119
120pub use xc3_write_derive::Xc3Write;
122pub use xc3_write_derive::Xc3WriteOffsets;
123
124pub struct FieldPosition<'a, T> {
125 pub position: u64,
127 pub data: &'a T,
129}
130
131impl<'a, T> FieldPosition<'a, T> {
132 pub fn new(position: u64, data: &'a T) -> Self {
133 Self { position, data }
134 }
135}
136
137pub struct Offset<'a, P, T> {
138 pub position: u64,
140 pub data: &'a T,
142 pub field_alignment: Option<u64>,
144 pub padding_byte: u8,
147 phantom: PhantomData<P>,
148}
149
150impl<P, T: Xc3Write> std::fmt::Debug for Offset<'_, P, T> {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 f.debug_struct("Offset")
154 .field("position", &self.position)
155 .field("data", &std::any::type_name::<T>())
156 .finish()
157 }
158}
159
160impl<'a, P, T> Offset<'a, P, T> {
161 pub fn new(position: u64, data: &'a T, field_alignment: Option<u64>, padding_byte: u8) -> Self {
162 Self {
163 position,
164 data,
165 field_alignment,
166 padding_byte,
167 phantom: PhantomData,
168 }
169 }
170
171 pub fn set_offset<W>(&self, writer: &mut W, offset: u64, endian: Endian) -> Xc3Result<()>
172 where
173 W: Write + Seek,
174 P: TryFrom<u64> + Xc3Write,
176 <P as TryFrom<u64>>::Error: std::fmt::Debug,
177 {
178 writer.seek(SeekFrom::Start(self.position))?;
179 let offset = P::try_from(offset).unwrap();
180 offset.xc3_write(writer, endian)?;
181 Ok(())
182 }
183
184 fn set_offset_seek<W>(
185 &self,
186 writer: &mut W,
187 base_offset: u64,
188 data_ptr: &mut u64,
189 type_alignment: u64,
190 should_write: bool,
191 endian: Endian,
192 ) -> Xc3Result<()>
193 where
194 W: Write + Seek,
195 P: TryFrom<u64> + Xc3Write,
197 <P as TryFrom<u64>>::Error: std::fmt::Debug,
198 {
199 *data_ptr = (*data_ptr).max(writer.stream_position()?);
201
202 let alignment = self.field_alignment.unwrap_or(type_alignment);
204 let aligned_data_pr = data_ptr.next_multiple_of(alignment);
205
206 self.set_offset(writer, aligned_data_pr - base_offset, endian)?;
208
209 if should_write {
210 writer.seek(SeekFrom::Start(*data_ptr))?;
213 vec![self.padding_byte; (aligned_data_pr - *data_ptr) as usize]
214 .xc3_write(writer, endian)?;
215 *data_ptr = (*data_ptr).max(writer.stream_position()?);
217 }
218
219 Ok(())
220 }
221}
222
223impl<P, T> Offset<'_, P, T>
224where
225 T: Xc3Write,
226 P: TryFrom<u64> + Xc3Write,
227 <P as TryFrom<u64>>::Error: std::fmt::Debug,
228{
229 pub fn write<W: Write + Seek>(
230 &self,
231 writer: &mut W,
232 base_offset: u64,
233 data_ptr: &mut u64,
234 endian: Endian,
235 ) -> Xc3Result<T::Offsets<'_>> {
236 if let Some(should_write) = self.data.should_write() {
237 self.set_offset_seek(
238 writer,
239 base_offset,
240 data_ptr,
241 T::ALIGNMENT,
242 should_write,
243 endian,
244 )?;
245 }
246 let offsets = self.data.xc3_write(writer, endian)?;
247 *data_ptr = (*data_ptr).max(writer.stream_position()?);
248 Ok(offsets)
249 }
250}
251
252impl<P, T> Offset<'_, P, T>
253where
254 T: Xc3Write + WriteFull,
255 P: TryFrom<u64> + Xc3Write,
256 <P as TryFrom<u64>>::Error: std::fmt::Debug,
257{
258 pub fn write_full<W: Write + Seek>(
259 &self,
260 writer: &mut W,
261 base_offset: u64,
262 data_ptr: &mut u64,
263 endian: Endian,
264 args: T::Args,
265 ) -> Xc3Result<()> {
266 if let Some(should_write) = self.data.should_write() {
268 self.set_offset_seek(
269 writer,
270 base_offset,
271 data_ptr,
272 T::ALIGNMENT,
273 should_write,
274 endian,
275 )?;
276 self.data
277 .write_full(writer, base_offset, data_ptr, endian, args)?;
278 }
279 Ok(())
280 }
281}
282
283macro_rules! xc3_write_impl {
284 ($($ty:ty),*) => {
285 $(
286 impl Xc3Write for $ty {
287 type Offsets<'a> = ();
289
290 fn xc3_write<W: std::io::Write + std::io::Seek>(
291 &self,
292 writer: &mut W,
293 endian: Endian,
294 ) -> Xc3Result<Self::Offsets<'_>> {
295 match endian {
296 Endian::Little => writer.write_all(&self.to_le_bytes())?,
297 Endian::Big => writer.write_all(&self.to_be_bytes())?,
298 }
299 Ok(())
300 }
301
302 const ALIGNMENT: u64 = std::mem::align_of::<$ty>() as u64;
304 }
305 )*
306
307 };
308}
309
310xc3_write_impl!(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64);
311
312impl<A: Xc3Write, B: Xc3Write> Xc3Write for (A, B) {
314 type Offsets<'a>
315 = (A::Offsets<'a>, B::Offsets<'a>)
316 where
317 A: 'a,
318 B: 'a;
319
320 fn xc3_write<W: Write + Seek>(
321 &self,
322 writer: &mut W,
323 endian: Endian,
324 ) -> Xc3Result<Self::Offsets<'_>> {
325 Ok((
326 self.0.xc3_write(writer, endian)?,
327 self.1.xc3_write(writer, endian)?,
328 ))
329 }
330}
331
332impl<A: Xc3WriteOffsets, B: Xc3WriteOffsets> Xc3WriteOffsets for (A, B) {
333 type Args = ();
334
335 fn write_offsets<W: Write + Seek>(
336 &self,
337 _: &mut W,
338 _: u64,
339 _: &mut u64,
340 _: Endian,
341 _: (),
342 ) -> Xc3Result<()> {
343 Ok(())
344 }
345}
346
347impl<const N: usize, T> Xc3Write for [T; N]
349where
350 T: Xc3Write + 'static,
351{
352 type Offsets<'a> = ();
353
354 fn xc3_write<W: Write + Seek>(
355 &self,
356 writer: &mut W,
357 endian: Endian,
358 ) -> Xc3Result<Self::Offsets<'_>> {
359 for value in self {
360 value.xc3_write(writer, endian)?;
361 }
362 Ok(())
363 }
364}
365
366impl<T: Xc3Write> Xc3Write for Box<T> {
367 type Offsets<'a>
368 = T::Offsets<'a>
369 where
370 Self: 'a;
371
372 fn xc3_write<W: Write + Seek>(
373 &self,
374 writer: &mut W,
375 endian: Endian,
376 ) -> Xc3Result<Self::Offsets<'_>> {
377 self.deref().xc3_write(writer, endian)
378 }
379}
380
381impl Xc3Write for String {
382 type Offsets<'a> = ();
383
384 fn xc3_write<W: Write + Seek>(
385 &self,
386 writer: &mut W,
387 _: Endian,
388 ) -> Xc3Result<Self::Offsets<'_>> {
389 writer.write_all(self.as_bytes())?;
390 writer.write_all(&[0u8])?;
391 Ok(())
392 }
393
394 const ALIGNMENT: u64 = 1;
395}
396
397pub struct VecOffsets<T>(pub Vec<T>);
400
401impl<T> Xc3Write for Vec<T>
402where
403 T: Xc3Write + 'static,
404{
405 type Offsets<'a> = VecOffsets<T::Offsets<'a>>;
406
407 fn xc3_write<W: Write + Seek>(
408 &self,
409 writer: &mut W,
410 endian: Endian,
411 ) -> Xc3Result<Self::Offsets<'_>> {
412 let offsets = if let Some(bytes) = <dyn core::any::Any>::downcast_ref::<Vec<u8>>(self) {
414 writer.write_all(bytes)?;
416 Vec::new()
417 } else {
418 self.iter()
419 .map(|v| v.xc3_write(writer, endian))
420 .collect::<Result<Vec<_>, _>>()?
421 };
422 Ok(VecOffsets(offsets))
423 }
424
425 fn should_write(&self) -> Option<bool> {
426 Some(!self.is_empty())
427 }
428}
429
430impl<T, A> Xc3WriteOffsets for VecOffsets<T>
431where
432 T: Xc3WriteOffsets<Args = A>,
433 A: Clone,
434{
435 type Args = A;
436
437 fn write_offsets<W: Write + Seek>(
438 &self,
439 writer: &mut W,
440 base_offset: u64,
441 data_ptr: &mut u64,
442 endian: Endian,
443 args: Self::Args,
444 ) -> Xc3Result<()> {
445 for item in &self.0 {
447 item.write_offsets(writer, base_offset, data_ptr, endian, args.clone())?;
448 }
449 Ok(())
450 }
451}
452
453impl Xc3Write for () {
454 type Offsets<'a> = ();
455
456 fn xc3_write<W: Write + Seek>(&self, _: &mut W, _: Endian) -> Xc3Result<Self::Offsets<'_>> {
457 Ok(())
458 }
459
460 const ALIGNMENT: u64 = 1;
461}
462
463impl Xc3WriteOffsets for () {
464 type Args = ();
465 fn write_offsets<W: Write + Seek>(
466 &self,
467 _: &mut W,
468 _: u64,
469 _: &mut u64,
470 _: Endian,
471 _: Self::Args,
472 ) -> Xc3Result<()> {
473 Ok(())
474 }
475}
476
477impl<T> Xc3Write for Option<T>
478where
479 T: Xc3Write,
480{
481 type Offsets<'a>
482 = Option<T::Offsets<'a>>
483 where
484 Self: 'a;
485
486 fn xc3_write<W: Write + Seek>(
487 &self,
488 writer: &mut W,
489 endian: Endian,
490 ) -> Xc3Result<Self::Offsets<'_>> {
491 self.as_ref()
492 .map(|v| v.xc3_write(writer, endian))
493 .transpose()
494 }
495
496 fn should_write(&self) -> Option<bool> {
497 self.as_ref().map(|_| true)
498 }
499
500 const ALIGNMENT: u64 = T::ALIGNMENT;
501}
502
503impl<T, A> Xc3WriteOffsets for Option<T>
504where
505 T: Xc3WriteOffsets<Args = A>,
506{
507 type Args = A;
508
509 fn write_offsets<W: Write + Seek>(
510 &self,
511 writer: &mut W,
512 base_offset: u64,
513 data_ptr: &mut u64,
514 endian: Endian,
515 args: Self::Args,
516 ) -> Xc3Result<()> {
517 if let Some(value) = self {
518 value.write_offsets(writer, base_offset, data_ptr, endian, args)?;
519 }
520 Ok(())
521 }
522}
523
524#[doc(hidden)]
525#[macro_export]
526macro_rules! assert_hex_eq {
527 ($a:expr, $b:expr) => {
528 pretty_assertions::assert_str_eq!(hex::encode($a), hex::encode($b))
529 };
530}