summaryrefslogtreecommitdiff
path: root/src/bin/br/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/br/config.rs')
-rw-r--r--src/bin/br/config.rs122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/bin/br/config.rs b/src/bin/br/config.rs
new file mode 100644
index 0000000..56d5190
--- /dev/null
+++ b/src/bin/br/config.rs
@@ -0,0 +1,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;
+}