use crate::{error::Error, util::read::Reader, version::Version};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct WindowsVersion {
pub build: u16,
pub minor: u8,
pub major: u8,
}
impl WindowsVersion {
pub(crate) fn read(reader: &mut Reader<'_>, version: &Version) -> Result<Self, Error> {
let build = if version.at_least(1, 3, 19) {
reader.u16_le("WindowsVersion.Build")?
} else {
0
};
let minor = reader.u8("WindowsVersion.Minor")?;
let major = reader.u8("WindowsVersion.Major")?;
Ok(Self {
build,
minor,
major,
})
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ServicePack {
pub minor: u8,
pub major: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Bitness {
InstallDefault,
Bits32,
Bits64,
Native,
CurrentProcess,
}
stable_name_enum!(Bitness, {
Self::InstallDefault => "install_default",
Self::Bits32 => "bits32",
Self::Bits64 => "bits64",
Self::Native => "native",
Self::CurrentProcess => "current_process",
});
impl Bitness {
pub(crate) fn decode(b: u8) -> Option<Self> {
match b {
0 => Some(Self::InstallDefault),
1 => Some(Self::Bits32),
2 => Some(Self::Bits64),
3 => Some(Self::Native),
4 => Some(Self::CurrentProcess),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TargetWindows {
pub windows: WindowsVersion,
pub nt: WindowsVersion,
pub service_pack: ServicePack,
}
impl TargetWindows {
pub(crate) fn read(reader: &mut Reader<'_>, version: &Version) -> Result<Self, Error> {
let windows = WindowsVersion::read(reader, version)?;
let nt = WindowsVersion::read(reader, version)?;
let service_pack = if version.at_least(1, 3, 19) {
ServicePack {
minor: reader.u8("ServicePack.Minor")?,
major: reader.u8("ServicePack.Major")?,
}
} else {
ServicePack::default()
};
Ok(Self {
windows,
nt,
service_pack,
})
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct WindowsVersionRange {
pub min: TargetWindows,
pub only_below: TargetWindows,
}
impl WindowsVersionRange {
pub(crate) fn read(reader: &mut Reader<'_>, version: &Version) -> Result<Self, Error> {
let min = TargetWindows::read(reader, version)?;
let only_below = TargetWindows::read(reader, version)?;
Ok(Self { min, only_below })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::version::{Version, VersionFlags};
fn modern_version() -> Version {
Version {
a: 6,
b: 4,
c: 0,
d: 1,
flags: VersionFlags::UNICODE,
raw_marker: [0u8; 64],
}
}
#[test]
fn windows_version_decodes_4_bytes() {
let bytes = [0x65, 0x4A, 0x00, 0x0A];
let mut r = Reader::new(&bytes);
let v = WindowsVersion::read(&mut r, &modern_version()).unwrap();
assert_eq!(v.build, 19045);
assert_eq!(v.minor, 0);
assert_eq!(v.major, 10);
assert_eq!(r.pos(), 4);
}
#[test]
fn target_windows_decodes_10_bytes() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&22000u16.to_le_bytes());
bytes.extend_from_slice(&[0, 10]);
bytes.extend_from_slice(&10240u16.to_le_bytes());
bytes.extend_from_slice(&[0, 10]);
bytes.extend_from_slice(&[1, 2]);
assert_eq!(bytes.len(), 10);
let mut r = Reader::new(&bytes);
let t = TargetWindows::read(&mut r, &modern_version()).unwrap();
assert_eq!(t.windows.build, 22000);
assert_eq!(t.nt.build, 10240);
assert_eq!(t.service_pack.minor, 1);
assert_eq!(t.service_pack.major, 2);
assert_eq!(r.pos(), 10);
}
#[test]
fn windows_version_range_decodes_20_bytes() {
let bytes = vec![0u8; 20];
let mut r = Reader::new(&bytes);
let _range = WindowsVersionRange::read(&mut r, &modern_version()).unwrap();
assert_eq!(r.pos(), 20);
}
}