use crate::*; const SMALL_ICON_LEN: usize = 3*3*8; const LARGE_ICON_LEN: usize = 8*8*8; pub fn parse_metadata(bytecode: &[u8]) -> Option { MetadataParser::from_bytecode(bytecode).parse() } pub struct ProgramMetadata { pub name: Option, pub version: Option, pub authors: Option>, pub description: Option, pub bg_colour: Option, pub fg_colour: Option, pub small_icon: Option<[u8; SMALL_ICON_LEN]>, pub large_icon: Option<[u8; LARGE_ICON_LEN]>, } struct MetadataParser<'a> { bytecode: &'a [u8], } impl<'a> MetadataParser<'a> { pub fn from_bytecode(bytecode: &'a [u8]) -> Self { Self { bytecode } } pub fn parse(self) -> Option { // Verify metadata identifier. let identifier = self.vec(0x00, 10); if identifier != &[0x41,0x00,0x18,0x42,0x45,0x44,0x52,0x4F,0x43,0x4B] { return None; } let (name, version) = if let Some(pointer) = self.pointer(0x0a) { let string = self.string(pointer); if let Some((name, version)) = string.split_once('/') { (Some(name.trim().to_string()), Some(version.trim().to_string())) } else { (Some(string.trim().to_string()), None) } } else { (None, None) }; let authors = self.pointer(0x0c).map(|p| { self.string(p).lines().map(|s| s.trim().to_string()).collect() }); let description = self.pointer(0x0e).map(|p| self.string(p)); let bg_colour = self.pointer(0x10).map(|p| self.colour(p)); let fg_colour = self.pointer(0x12).map(|p| self.colour(p)); let small_icon = if let Some(pointer) = self.pointer(0x14) { let vec = self.vec(pointer, SMALL_ICON_LEN); Some(vec.try_into().unwrap()) } else { None }; let large_icon = if let Some(pointer) = self.pointer(0x16) { let vec = self.vec(pointer, LARGE_ICON_LEN); Some(vec.try_into().unwrap()) } else { None }; let metadata = ProgramMetadata { name, version, authors, description, bg_colour, fg_colour, small_icon, large_icon, }; return Some(metadata); } fn byte(&self, address: usize) -> u8 { match self.bytecode.get(address) { Some(byte) => *byte, None => 0, } } fn double(&self, address: usize) -> u16 { u16::from_be_bytes([ self.byte(address), self.byte(address + 1), ]) } fn pointer(&self, address: usize) -> Option { match self.double(address) { 0 => None, v => Some(v as usize), } } fn vec(&self, address: usize, length: usize) -> Vec { let mut vec = Vec::new(); for i in 0..length { vec.push(self.byte(address + i)); } return vec; } fn string(&self, address: usize) -> String { let mut i = address; while self.byte(i) != 0 { i += 1; } let slice = &self.bytecode[address..i]; String::from_utf8_lossy(slice).to_string() } fn colour(&self, address: usize) -> Colour { let double = self.double(address); let r = (double >> 8 & 0xf) as u8 * 17; let g = (double >> 4 & 0xf) as u8 * 17; let b = (double & 0xf) as u8 * 17; Colour::from_rgb(r, g, b) } }