summaryrefslogtreecommitdiff
path: root/src/types/metadata.rs
blob: dde86fa94ccc2c95f0bddd6e5df3d1b034633e2e (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
123
124
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)
    }
}