summaryrefslogtreecommitdiff
path: root/src/types/metadata.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/types/metadata.rs')
-rw-r--r--src/types/metadata.rs125
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)
+ }
+}