1use std::ffi::{CStr, FromBytesUntilNulError};
4use std::marker::PhantomData;
5use std::path::Path;
6use zerocopy::{
7 byteorder::network_endian::{U16, U32},
8 *,
9};
10
11#[repr(C)]
12#[derive(derive_more::Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
13pub struct Offset<V, T: ?Sized> {
14 pub offset: V,
15 #[debug(skip)]
16 marker: PhantomData<T>,
17}
18
19impl<V, T> Offset<V, T>
20where
21 V: Into<u32> + Copy,
22 T: TryFromBytes + KnownLayout + Immutable + ?Sized,
23{
24 pub fn at<'a>(&self, bytes: &'a [u8]) -> Result<&'a T, TryCastError<&'a [u8], T>> {
25 let offset = self.offset.into() as usize;
26 T::try_ref_from_prefix(&bytes[offset..]).map(|(t, _)| t)
27 }
28}
29
30impl<V> Offset<V, CStr>
31where
32 V: Into<u32> + Copy,
33{
34 pub fn str_at<'a>(&self, bytes: &'a [u8]) -> Result<&'a CStr, FromBytesUntilNulError> {
35 let offset = self.offset.into() as usize;
36 CStr::from_bytes_until_nul(&bytes[offset..])
37 }
38}
39
40impl<V> Offset<V, Path>
41where
42 V: Into<u32> + Copy,
43{
44 pub fn path_at<'a>(&self, bytes: &'a [u8]) -> Option<&'a Path> {
45 let offset = self.offset.into() as usize;
46 let cstr = CStr::from_bytes_until_nul(&bytes[offset..]).ok()?;
47 let str = cstr.to_str().ok()?;
48 Some(Path::new(str))
49 }
50}
51
52impl<V, T: ?Sized> Clone for Offset<V, T>
53where
54 V: Clone,
55{
56 fn clone(&self) -> Self {
57 Self {
58 offset: self.offset.clone(),
59 marker: Default::default(),
60 }
61 }
62}
63
64impl<V, T: ?Sized> Copy for Offset<V, T> where V: Copy {}
65
66impl<V, T> Offset<V, T>
67where
68 T: ?Sized,
69{
70 pub fn new(offset: impl Into<V>) -> Self {
71 Self {
72 offset: offset.into(),
73 marker: Default::default(),
74 }
75 }
76}
77
78impl<T> Offset<U32, T>
79where
80 T: ?Sized,
81{
82 #[inline(always)]
83 pub fn is_null(&self) -> bool {
84 self.offset == 0 || self.offset == 0xFFFFFFFF
85 }
86}
87
88#[repr(C)]
89#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
90pub struct Header {
91 pub major_version: U16,
92 pub minor_version: U16,
93 pub hash: Offset<U32, Hash>,
94 pub directory_list: Offset<U32, DirectoryList>,
95}
96
97#[repr(C)]
98#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
99pub struct DirectoryList {
100 pub n_directories: U32,
101 pub directory: [Offset<U32, Path>],
102}
103
104#[repr(C)]
105#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
106pub struct Hash {
107 pub n_buckets: U32,
108 pub icon: [Offset<U32, Icon>],
109}
110
111#[repr(C)]
112#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
113pub struct Icon {
114 pub chain: Offset<U32, Icon>,
115 pub name: Offset<U32, CStr>,
116 pub image_list: Offset<U32, ImageList>,
117}
118
119impl Icon {
120 pub(crate) fn iter<'a>(&'a self, bytes: &'a [u8]) -> impl Iterator<Item = &'a Icon> {
121 let mut icon = Some(self);
122
123 std::iter::from_fn(move || {
124 let result = icon;
125
126 if let Some(result) = result {
127 if result.chain.is_null() {
128 icon = None; return Some(result);
130 }
131
132 icon = result.chain.at(bytes).ok()
133 }
134
135 result
136 })
137 }
138}
139
140#[repr(C)]
141#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
142pub struct ImageList {
143 pub n_images: U32,
144 pub images: [Image],
145}
146
147#[repr(C)]
148#[derive(Debug, Copy, Clone, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
149pub struct Image {
150 pub directory_index: U16,
151 pub icon_flags: Flags,
152 pub image_data: Offset<U32, ImageData>,
153}
154
155#[repr(C)]
156#[derive(Debug, Copy, Clone, Default, FromBytes, Immutable, Eq, PartialEq)]
157pub struct Flags {
158 value: U16,
159}
160
161impl Flags {
162 pub const HAS_SUFFIX_XPM: U16 = U16::new(1);
163 pub const HAS_SUFFIX_SVG: U16 = U16::new(2);
164 pub const HAS_SUFFIX_PNG: U16 = U16::new(4);
165 pub const HAS_ICON_FILE: U16 = U16::new(8);
166
167 pub fn new(value: U16) -> Self {
168 Flags { value }
169 }
170
171 pub fn bits(&self) -> U16 {
172 self.value
173 }
174
175 pub fn has_suffix_xpm(&self) -> bool {
176 (self.value & Self::HAS_SUFFIX_XPM) != 0
177 }
178
179 pub fn has_suffix_svg(&self) -> bool {
180 (self.value & Self::HAS_SUFFIX_SVG) != 0
181 }
182
183 pub fn has_suffix_png(&self) -> bool {
184 (self.value & Self::HAS_SUFFIX_PNG) != 0
185 }
186}
187
188#[repr(C)]
189#[derive(Debug, Copy, Clone, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
190pub struct ImageData {
191 pub image_pixel_data: Offset<U32, ()>,
192 pub image_meta_data: Offset<U32, MetaData>,
193 pub image_pixel_data_type: Offset<U32, ()>,
194 pub image_pixel_data_length: Offset<U32, ()>,
195 }
197
198#[repr(C)]
199#[derive(Debug, Copy, Clone, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
200pub struct MetaData {
201 pub embedded_rect: Offset<U32, EmbeddedRect>,
202 pub attach_point_list: Offset<U32, AttachPointList>,
203 pub display_name_list: Offset<U32, DisplayNameList>,
204}
205
206#[repr(C)]
207#[derive(Debug, Copy, Clone, Default, FromBytes, Immutable, Eq, PartialEq)]
208pub struct EmbeddedRect {
209 pub x0: U16,
210 pub y0: U16,
211 pub x1: U16,
212 pub y1: U16,
213}
214
215#[repr(C)]
216#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
217pub struct AttachPointList {
218 pub n_attach_points: U32,
219 pub attach_points: [AttachPoint],
220}
221
222#[repr(C)]
223#[derive(Debug, Copy, Clone, Default, FromBytes, Immutable, Eq, PartialEq)]
224pub struct AttachPoint {
225 pub x: U16,
226 pub y: U16,
227}
228
229#[repr(C)]
230#[derive(Debug, FromBytes, KnownLayout, Immutable, Eq, PartialEq)]
231pub struct DisplayNameList {
232 pub n_display_names: U32,
233 pub display_name: [DisplayName],
234}
235
236#[repr(C)]
237#[derive(Debug, Copy, Clone, FromBytes, Immutable, Eq, PartialEq)]
238pub struct DisplayName {
239 pub display_lang: Offset<U32, CStr>,
240 pub display_name: Offset<U32, CStr>,
241}