diff options
Diffstat (limited to 'src/types/metadata.rs')
-rw-r--r-- | src/types/metadata.rs | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/types/metadata.rs b/src/types/metadata.rs new file mode 100644 index 0000000..dde86fa --- /dev/null +++ b/src/types/metadata.rs @@ -0,0 +1,125 @@ +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<ProgramMetadata> { + MetadataParser::from_bytecode(bytecode).parse() +} + + +pub struct ProgramMetadata { + pub name: Option<String>, + pub version: Option<String>, + pub authors: Option<Vec<String>>, + pub description: Option<String>, + pub bg_colour: Option<Colour>, + pub fg_colour: Option<Colour>, + 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<ProgramMetadata> { + // 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<usize> { + match self.double(address) { + 0 => None, + v => Some(v as usize), + } + } + + fn vec(&self, address: usize, length: usize) -> Vec<u8> { + 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) + } +} |