nes_core/
genie.rs

1use alloc::collections::BTreeMap;
2use alloc::string::String;
3use alloc::vec::Vec;
4use serde::{Deserialize, Serialize};
5use anyhow::{Result, bail};
6use lazy_static::lazy_static;
7
8lazy_static! {
9    static ref GENIE_MAP: BTreeMap<char, u8> = {
10        btree_map! {
11            'A' => 0x0, 'P' => 0x1, 'Z' => 0x2, 'L' => 0x3, 'G' => 0x4, 'I' => 0x5, 'T' => 0x6,
12            'Y' => 0x7, 'E' => 0x8, 'O' => 0x9, 'X' => 0xA, 'U' => 0xB, 'K' => 0xC, 'S' => 0xD,
13            'V' => 0xE, 'N' => 0xF
14        }
15    };
16}
17
18/// Game Genie Code
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct GenieCode {
21    code: String,
22    addr: u16,
23    data: u8,
24    compare: Option<u8>,
25}
26
27impl GenieCode {
28    /// Creates a new `GenieCode` instance.
29    ///
30    /// # Errors
31    ///
32    /// This function will return an error if the given code is not the correct format.
33    pub fn new(code: String) -> Result<Self> {
34        let hex = Self::parse(&code)?;
35        let addr = 0x8000
36            + (((u16::from(hex[3]) & 7) << 12)
37            | ((u16::from(hex[5]) & 7) << 8)
38            | ((u16::from(hex[4]) & 8) << 8)
39            | ((u16::from(hex[2]) & 7) << 4)
40            | ((u16::from(hex[1]) & 8) << 4)
41            | (u16::from(hex[4]) & 7)
42            | (u16::from(hex[3]) & 8));
43        let data = if hex.len() == 6 {
44            ((hex[1] & 7) << 4) | ((hex[0] & 8) << 4) | (hex[0] & 7) | (hex[5] & 8)
45        } else {
46            ((hex[1] & 7) << 4) | ((hex[0] & 8) << 4) | (hex[0] & 7) | (hex[7] & 8)
47        };
48        let compare = if hex.len() == 8 {
49            Some(((hex[7] & 7) << 4) | ((hex[6] & 8) << 4) | (hex[6] & 7) | (hex[5] & 8))
50        } else {
51            None
52        };
53        Ok(Self {
54            code,
55            addr,
56            data,
57            compare,
58        })
59    }
60
61    pub fn parse(code: &str) -> Result<Vec<u8>> {
62        if code.len() != 6 && code.len() != 8 {
63            bail!("invalid game genie code: {code}. Length must be 6 or 8 characters.")
64        }
65        let mut hex: Vec<u8> = Vec::with_capacity(code.len());
66        for s in code.chars() {
67            if let Some(h) = GENIE_MAP.get(&s) {
68                hex.push(*h);
69            } else {
70                bail!( "invalid game genie code: {code}. Invalid character: {s}")
71            }
72        }
73        Ok(hex)
74    }
75
76    #[must_use]
77    pub fn code(&self) -> &str {
78        &self.code
79    }
80
81    #[must_use]
82    pub const fn addr(&self) -> u16 {
83        self.addr
84    }
85
86    #[must_use]
87    pub const fn read(&self, val: u8) -> u8 {
88        if let Some(compare) = self.compare {
89            if val == compare {
90                self.data
91            } else {
92                val
93            }
94        } else {
95            self.data
96        }
97    }
98}
99
100impl core::fmt::Display for GenieCode {
101    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102        write!(f, "{}", &self.code)
103    }
104}