1use crate::TagType;
4use crate::tag::TagHeader;
5use core::fmt::Debug;
6use core::mem;
7use core::slice;
8use multiboot2_common::{MaybeDynSized, Tag};
9use thiserror::Error;
10#[cfg(feature = "builder")]
11use {alloc::boxed::Box, multiboot2_common::new_boxed};
12
13struct Reader<'a> {
16    buffer: &'a [u8],
17    off: usize,
18}
19
20impl<'a> Reader<'a> {
21    const fn new(buffer: &'a [u8]) -> Self {
22        Self { buffer, off: 0 }
23    }
24
25    fn read_next_u8(&mut self) -> u8 {
31        let val = self
32            .buffer
33            .get(self.off)
34            .cloned()
35            .expect("Embedded framebuffer info should be properly sized and available");
40        self.off += 1;
41        val
42    }
43
44    fn read_next_u16(&mut self) -> u16 {
50        let u16_lo = self.read_next_u8() as u16;
51        let u16_hi = self.read_next_u8() as u16;
52        (u16_hi << 8) | u16_lo
53    }
54
55    const fn current_ptr(&self) -> *const u8 {
56        unsafe { self.buffer.as_ptr().add(self.off) }
57    }
58}
59
60#[derive(ptr_meta::Pointee, Eq)]
62#[repr(C, align(8))]
63pub struct FramebufferTag {
64    header: TagHeader,
65
66    address: u64,
72
73    pitch: u32,
75
76    width: u32,
78
79    height: u32,
81
82    bpp: u8,
84
85    framebuffer_type: FramebufferTypeId,
91
92    _padding: u16,
93
94    buffer: [u8],
96}
97
98impl FramebufferTag {
99    #[cfg(feature = "builder")]
101    #[must_use]
102    pub fn new(
103        address: u64,
104        pitch: u32,
105        width: u32,
106        height: u32,
107        bpp: u8,
108        buffer_type: FramebufferType,
109    ) -> Box<Self> {
110        let header = TagHeader::new(Self::ID, 0);
111        let address = address.to_ne_bytes();
112        let pitch = pitch.to_ne_bytes();
113        let width = width.to_ne_bytes();
114        let height = height.to_ne_bytes();
115        let buffer_type_id = buffer_type.id();
116        let padding = [0; 2];
117        let optional_buffer = buffer_type.serialize();
118        new_boxed(
119            header,
120            &[
121                &address,
122                &pitch,
123                &width,
124                &height,
125                &[bpp],
126                &[buffer_type_id as u8],
127                &padding,
128                &optional_buffer,
129            ],
130        )
131    }
132
133    #[must_use]
139    pub const fn address(&self) -> u64 {
140        self.address
141    }
142
143    #[must_use]
145    pub const fn pitch(&self) -> u32 {
146        self.pitch
147    }
148
149    #[must_use]
151    pub const fn width(&self) -> u32 {
152        self.width
153    }
154
155    #[must_use]
157    pub const fn height(&self) -> u32 {
158        self.height
159    }
160
161    #[must_use]
163    pub const fn bpp(&self) -> u8 {
164        self.bpp
165    }
166
167    pub fn buffer_type(&self) -> Result<FramebufferType, UnknownFramebufferType> {
169        let mut reader = Reader::new(&self.buffer);
170
171        let fb_type_raw = self.framebuffer_type as u8;
174        let fb_type = FramebufferTypeId::try_from(fb_type_raw)?;
175
176        match fb_type {
177            FramebufferTypeId::Indexed => {
178                let num_colors = reader.read_next_u16();
182
183                let palette = {
184                    assert_eq!(mem::size_of::<FramebufferColor>(), 3);
186
187                    unsafe {
188                        slice::from_raw_parts(
189                            reader.current_ptr().cast::<FramebufferColor>(),
190                            num_colors as usize,
191                        )
192                    }
193                };
194                Ok(FramebufferType::Indexed { palette })
195            }
196            FramebufferTypeId::RGB => {
197                let red_pos = reader.read_next_u8(); let red_mask = reader.read_next_u8(); let green_pos = reader.read_next_u8();
200                let green_mask = reader.read_next_u8();
201                let blue_pos = reader.read_next_u8();
202                let blue_mask = reader.read_next_u8();
203                Ok(FramebufferType::RGB {
204                    red: FramebufferField {
205                        position: red_pos,
206                        size: red_mask,
207                    },
208                    green: FramebufferField {
209                        position: green_pos,
210                        size: green_mask,
211                    },
212                    blue: FramebufferField {
213                        position: blue_pos,
214                        size: blue_mask,
215                    },
216                })
217            }
218            FramebufferTypeId::Text => Ok(FramebufferType::Text),
219        }
220    }
221}
222
223impl MaybeDynSized for FramebufferTag {
224    type Header = TagHeader;
225
226    const BASE_SIZE: usize = mem::size_of::<TagHeader>()
227        + mem::size_of::<u64>()
228        + 3 * mem::size_of::<u32>()
229        + 2 * mem::size_of::<u8>()
230        + mem::size_of::<u16>();
231
232    fn dst_len(header: &TagHeader) -> usize {
233        assert!(header.size as usize >= Self::BASE_SIZE);
234        header.size as usize - Self::BASE_SIZE
235    }
236}
237
238impl Tag for FramebufferTag {
239    type IDType = TagType;
240
241    const ID: TagType = TagType::Framebuffer;
242}
243
244impl Debug for FramebufferTag {
245    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
246        f.debug_struct("FramebufferTag")
247            .field("typ", &self.header.typ)
248            .field("size", &self.header.size)
249            .field("buffer_type", &self.buffer_type())
250            .field("address", &self.address)
251            .field("pitch", &self.pitch)
252            .field("width", &self.width)
253            .field("height", &self.height)
254            .field("bpp", &self.bpp)
255            .finish()
256    }
257}
258
259impl PartialEq for FramebufferTag {
260    fn eq(&self, other: &Self) -> bool {
261        self.header == other.header
262            && self.address == { other.address }
263            && self.pitch == { other.pitch }
264            && self.width == { other.width }
265            && self.height == { other.height }
266            && self.bpp == { other.bpp }
267            && self.framebuffer_type == { other.framebuffer_type }
268            && self.buffer == other.buffer
269    }
270}
271
272#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
274#[repr(u8)]
275#[allow(clippy::upper_case_acronyms)]
276pub enum FramebufferTypeId {
277    Indexed = 0,
278    RGB = 1,
279    Text = 2,
280    }
282
283impl TryFrom<u8> for FramebufferTypeId {
284    type Error = UnknownFramebufferType;
285
286    fn try_from(value: u8) -> Result<Self, Self::Error> {
287        match value {
288            0 => Ok(Self::Indexed),
289            1 => Ok(Self::RGB),
290            2 => Ok(Self::Text),
291            val => Err(UnknownFramebufferType(val)),
292        }
293    }
294}
295
296impl From<FramebufferType<'_>> for FramebufferTypeId {
297    fn from(value: FramebufferType) -> Self {
298        match value {
299            FramebufferType::Indexed { .. } => Self::Indexed,
300            FramebufferType::RGB { .. } => Self::RGB,
301            FramebufferType::Text => Self::Text,
302        }
303    }
304}
305
306#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub enum FramebufferType<'a> {
310    Indexed {
312        #[allow(missing_docs)]
313        palette: &'a [FramebufferColor],
314    },
315
316    #[allow(missing_docs)]
318    #[allow(clippy::upper_case_acronyms)]
319    RGB {
320        red: FramebufferField,
321        green: FramebufferField,
322        blue: FramebufferField,
323    },
324
325    Text,
332}
333
334impl FramebufferType<'_> {
335    #[must_use]
336    #[cfg(feature = "builder")]
337    const fn id(&self) -> FramebufferTypeId {
338        match self {
339            FramebufferType::Indexed { .. } => FramebufferTypeId::Indexed,
340            FramebufferType::RGB { .. } => FramebufferTypeId::RGB,
341            FramebufferType::Text => FramebufferTypeId::Text,
342        }
343    }
344
345    #[must_use]
346    #[cfg(feature = "builder")]
347    fn serialize(&self) -> alloc::vec::Vec<u8> {
348        let mut data = alloc::vec::Vec::new();
349        match self {
350            FramebufferType::Indexed { palette } => {
351                let num_colors = palette.len() as u16;
355                data.extend(&num_colors.to_ne_bytes());
356                for color in *palette {
357                    let serialized_color = [color.red, color.green, color.blue];
358                    data.extend(&serialized_color);
359                }
360            }
361            FramebufferType::RGB { red, green, blue } => data.extend(&[
362                red.position,
363                red.size,
364                green.position,
365                green.size,
366                blue.position,
367                blue.size,
368            ]),
369            FramebufferType::Text => {}
370        }
371        data
372    }
373}
374
375#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
377#[repr(C)]
378pub struct FramebufferField {
379    pub position: u8,
381
382    pub size: u8,
384}
385
386#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
392#[repr(C)] pub struct FramebufferColor {
394    pub red: u8,
396
397    pub green: u8,
399
400    pub blue: u8,
402}
403
404#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
406#[error("Unknown framebuffer type {0}")]
407pub struct UnknownFramebufferType(u8);
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412
413    #[test]
415    fn test_size() {
416        assert_eq!(mem::size_of::<FramebufferColor>(), 3)
417    }
418
419    #[test]
420    #[cfg(feature = "builder")]
421    fn create_new() {
422        let tag = FramebufferTag::new(0x1000, 1, 1024, 1024, 8, FramebufferType::Text);
423        dbg!(tag);
425
426        let tag = FramebufferTag::new(
427            0x1000,
428            1,
429            1024,
430            1024,
431            8,
432            FramebufferType::Indexed {
433                palette: &[
434                    FramebufferColor {
435                        red: 255,
436                        green: 255,
437                        blue: 255,
438                    },
439                    FramebufferColor {
440                        red: 127,
441                        green: 42,
442                        blue: 73,
443                    },
444                ],
445            },
446        );
447        dbg!(tag);
449
450        let tag = FramebufferTag::new(
451            0x1000,
452            1,
453            1024,
454            1024,
455            8,
456            FramebufferType::RGB {
457                red: FramebufferField {
458                    position: 0,
459                    size: 0,
460                },
461                green: FramebufferField {
462                    position: 10,
463                    size: 20,
464                },
465                blue: FramebufferField {
466                    position: 30,
467                    size: 40,
468                },
469            },
470        );
471        dbg!(tag);
473    }
474}