summaryrefslogtreecommitdiff
path: root/src/bin/br/config.rs
blob: 56d5190839abb31e2256622322597559bad15ca4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::*;


#[derive(Copy, Clone, PartialEq)]
pub enum Mode {
    Graphical,
    Headless,
    Dynamic,
}

impl Mode {
    pub fn from_str(string: &str) -> Self {
        match string {
            "g" | "graphical" => Self::Graphical,
            "h" | "headless" => Self::Headless,
            "d" | "dynamic" => Self::Dynamic,
            _ => fatal!("Invalid mode string '{string}'"),
        }
    }
}


pub fn parse_dimensions(string: &str) -> ScreenDimensions {
    fn parse_inner(string: &str) -> Option<ScreenDimensions> {
        let (w_str, h_str) = string.trim().split_once('x')?;
        Some( ScreenDimensions {
            width:  w_str.parse().ok()?,
            height: h_str.parse().ok()?,
        } )
    }
    let dimensions = parse_inner(string).unwrap_or_else(|| {
        fatal!("Invalid dimensions string '{string}'");
    });
    if dimensions.is_zero() {
        fatal!("Screen dimensions must be greater than zero");
    }
    return dimensions;
}


pub fn parse_palette(string: &str) -> [Colour; 16] {
    fn decode_ascii_hex_digit(ascii: u8) -> Option<u8> {
        match ascii {
            b'0'..=b'9' => Some(ascii - b'0'),
            b'a'..=b'f' => Some(ascii - b'a' + 10),
            b'A'..=b'F' => Some(ascii - b'A' + 10),
            _ => { None }
        }
    }
    fn parse_inner(string: &str) -> Option<[Colour; 16]> {
        let mut c = Vec::new();
        for token in string.split(',') {
            let mut bytes = token.bytes();
            if bytes.len() != 3 { return None; }
            let r = decode_ascii_hex_digit(bytes.next().unwrap())?;
            let g = decode_ascii_hex_digit(bytes.next().unwrap())?;
            let b = decode_ascii_hex_digit(bytes.next().unwrap())?;
            c.push(Colour::from_rgb(r*17, g*17, b*17));
        }
        Some(match c.len() {
            2 => [ c[0],  c[1],  c[0],  c[1],  c[0],  c[1],  c[0],  c[1],
                   c[0],  c[1],  c[0],  c[1],  c[0],  c[1],  c[0],  c[1] ],
            3 => [ c[0],  c[1],  c[0],  c[2],  c[0],  c[1],  c[0],  c[2],
                   c[0],  c[1],  c[0],  c[2],  c[0],  c[1],  c[0],  c[2] ],
            4 => [ c[0],  c[1],  c[2],  c[3],  c[0],  c[1],  c[2],  c[3],
                   c[0],  c[1],  c[2],  c[3],  c[0],  c[1],  c[2],  c[3] ],
            8 => [ c[0],  c[1],  c[2],  c[3],  c[4],  c[5],  c[6],  c[7],
                   c[0],  c[1],  c[2],  c[3],  c[4],  c[5],  c[6],  c[7] ],
           16 => [ c[0],  c[1],  c[2],  c[3],  c[4],  c[5],  c[6],  c[7],
                   c[8],  c[9], c[10], c[11], c[12], c[13], c[14], c[15] ],
            _ => return None,
        })
    }
    parse_inner(string).unwrap_or_else(|| {
        fatal!("Invalid palette string '{string}'");
    })
}


pub fn parse_metadata_colour(colour: Option<MetadataColour>) -> Option<Colour> {
    let c = colour?;
    Some(Colour::from_rgb(c.red, c.green, c.blue))
}


pub fn parse_small_icon(bytes: Option<Vec<u8>>, bg: Colour, fg: Colour) -> Option<Icon> {
    let rgba = sprite_data_to_rgb(&bytes?, 3, bg, fg);
    match Icon::from_rgba(rgba, 24, 24) {
        Ok(icon) => Some(icon),
        Err(err) => unreachable!("Error while parsing small icon data: {err}"),
    }
}

pub fn parse_large_icon(bytes: Option<Vec<u8>>, bg: Colour, fg: Colour) -> Option<Icon> {
    let rgba = sprite_data_to_rgb(&bytes?, 8, bg, fg);
    match Icon::from_rgba(rgba, 64, 64) {
        Ok(icon) => Some(icon),
        Err(err) => unreachable!("Error while parsing large icon data: {err}"),
    }
}

fn sprite_data_to_rgb(bytes: &[u8], size: usize, bg: Colour, fg: Colour) -> Vec<u8> {
    let sprites: Vec<&[u8]> = bytes.chunks_exact(8).collect();
    let mut rgba = Vec::new();
    for sprite_row in 0..size {
        for pixel_row in 0..8 {
            for sprite_column in 0..size {
                let sprite = &sprites[sprite_column + (sprite_row * size)];
                let row = &sprite[pixel_row];
                for bit in 0..8 {
                    let state = row & (0x80 >> bit);
                    let colour = match state != 0 {
                        true => fg,
                        false => bg,
                    };
                    rgba.extend_from_slice(&colour.as_rgba_array());
                }
            }
        }
    }
    return rgba;
}