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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Utilities to get information about the operating system and hardware state.

/// System version information. This struct is used for both kernel and firmware versions.
///
/// # Example
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// let firm_version = ctru::os::firm_version();
/// assert_ne!(firm_version.major(), 0);
///
/// let kernel_version = ctru::os::kernel_version();
/// assert_ne!(kernel_version.major(), 0);
/// ```
#[derive(Clone, Copy)]
pub struct Version(u32);

impl Version {
    /// Pack a system version from its components
    pub fn new(major: u8, minor: u8, revision: u8) -> Self {
        let major = u32::from(major);
        let minor = u32::from(minor);
        let revision = u32::from(revision);

        Self(major << 24 | minor << 16 | revision << 8)
    }

    /// Get the major version from a packed system version.
    pub fn major(&self) -> u8 {
        (self.0 >> 24).try_into().unwrap()
    }

    /// Get the minor version from a packed system version.
    pub fn minor(&self) -> u8 {
        (self.0 >> 16 & 0xFF).try_into().unwrap()
    }

    /// Get the revision from a packed system version.
    pub fn revision(&self) -> u8 {
        (self.0 >> 8 & 0xFF).try_into().unwrap()
    }
}

/// Get the system's FIRM version.
pub fn firm_version() -> Version {
    Version(unsafe { ctru_sys::osGetFirmVersion() })
}

/// Get the system's kernel version.
pub fn kernel_version() -> Version {
    Version(unsafe { ctru_sys::osGetKernelVersion() })
}

// TODO: I can't seem to find good documentation on it, but we could probably
// define enums for firmware type (NATIVE_FIRM, SAFE_FIRM etc.) as well as
// application memory layout. Leaving those as future enhancements for now

/// A region of memory. Most applications will only use [`Application`](MemRegion::Application)
/// memory, but the other types can be used to query memory usage information.
/// See <https://www.3dbrew.org/wiki/Memory_layout#FCRAM_memory-regions_layout>
/// for more details on the different types of memory.
///
/// # Example
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// let all_memory = ctru::os::MemRegion::All;
///
/// assert!(all_memory.size() > 0);
/// assert!(all_memory.used() > 0);
/// assert!(all_memory.free() > 0);
/// ```
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum MemRegion {
    /// All memory regions.
    All = ctru_sys::MEMREGION_ALL,
    /// APPLICATION memory.
    Application = ctru_sys::MEMREGION_APPLICATION,
    /// SYSTEM memory.
    System = ctru_sys::MEMREGION_SYSTEM,
    /// BASE memory.
    Base = ctru_sys::MEMREGION_BASE,
}

impl MemRegion {
    /// Get the total size of this memory region, in bytes.
    pub fn size(&self) -> usize {
        unsafe { ctru_sys::osGetMemRegionSize(*self as u8) }
            .try_into()
            .unwrap()
    }

    /// Get the number of bytes used within this memory region.
    pub fn used(&self) -> usize {
        unsafe { ctru_sys::osGetMemRegionUsed(*self as u8) }
            .try_into()
            .unwrap()
    }

    /// Get the number of bytes free within this memory region.
    pub fn free(&self) -> usize {
        unsafe { ctru_sys::osGetMemRegionFree(*self as u8) }
            .try_into()
            .unwrap()
    }
}

/// WiFi signal strength. This enum's `u8` representation corresponds with
/// the number of bars displayed in the Home menu.
///
/// # Example
///
/// ```
/// let _runner = test_runner::GdbRunner::default();
/// let strength = ctru::os::WifiStrength::current();
/// assert!((strength as u8) < 4);
/// ```
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum WifiStrength {
    /// This may indicate a very poor signal quality even worse than `Bad`,
    /// or that no network is connected at all.
    Disconnected = 0,
    /// Poor signal strength.
    Bad = 1,
    /// Medium signal strength.
    Decent = 2,
    /// Good signal strength.
    Good = 3,
}

impl WifiStrength {
    /// Get the current WiFi signal strength.
    pub fn current() -> Self {
        match unsafe { ctru_sys::osGetWifiStrength() } {
            0 => Self::Disconnected,
            1 => Self::Bad,
            2 => Self::Decent,
            3 => Self::Good,
            other => panic!("Got unexpected WiFi strength value {other}"),
        }
    }
}

/// Get the current value of the stereoscopic 3D slider on a scale from 0.0­–­1.0.
pub fn current_3d_slider_state() -> f32 {
    unsafe { ctru_sys::osGet3DSliderState() }
}

/// Whether or not a headset is currently plugged into the device.
pub fn is_headset_connected() -> bool {
    unsafe { ctru_sys::osIsHeadsetConnected() }
}