#![warn(missing_docs)]
#![forbid(unsafe_code)]
use anyhow::Context;
use mesh::MeshPayload;
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
mod resource_dll_parser;
#[derive(Debug, MeshPayload)]
pub struct RomFileLocation {
pub file: File,
pub start: u64,
pub len: usize,
}
fn system32_path() -> String {
let windows_dir = std::env::var("SystemRoot").unwrap_or(String::from(r"C:\Windows"));
format!("{windows_dir}\\System32")
}
pub fn find_pcat_bios(command_line_path: Option<&Path>) -> anyhow::Result<RomFileLocation> {
const NAME: &str = "pcat_firmware";
const BIOS_DESCRIPTOR: DllResourceDescriptor = DllResourceDescriptor::new(b"VMFW", 13500);
const EXPECTED_BIOS_SIZE: usize = 256 * 1024;
if let Some(p) = command_line_path {
parse_rom_file(p, NAME, BIOS_DESCRIPTOR, EXPECTED_BIOS_SIZE)
} else {
let system32_path = system32_path();
let default_pcat_firmware_file = format!(r"{system32_path}\vmfirmwarepcat.dll");
let result = match parse_rom_file(
&translate_path(Path::new(default_pcat_firmware_file.as_str()))?,
NAME,
BIOS_DESCRIPTOR,
EXPECTED_BIOS_SIZE,
) {
Ok(r) => r,
Err(_) => {
let legacy_pcat_firmware_file = format!(r"{system32_path}\vmfirmware.dll");
parse_rom_file(
&translate_path(Path::new(legacy_pcat_firmware_file.as_str()))?,
NAME,
BIOS_DESCRIPTOR,
EXPECTED_BIOS_SIZE,
)?
}
};
Ok(result)
}
}
pub fn find_svga_bios(command_line_path: Option<&Path>) -> anyhow::Result<RomFileLocation> {
const SVGA_BIOS_DESCRIPTOR: DllResourceDescriptor = DllResourceDescriptor::new(b"BIOS", 13501);
const NAME: &str = "vga_firmware";
const EXPECTED_SIZE: usize = 48 * 1024;
if let Some(p) = command_line_path {
parse_rom_file(p, NAME, SVGA_BIOS_DESCRIPTOR, EXPECTED_SIZE)
} else {
let system32_path = system32_path();
let default_svga_firmware_file = format!(r"{system32_path}\vmemulateddevices.dll");
parse_rom_file(
&translate_path(Path::new(default_svga_firmware_file.as_str()))?,
NAME,
SVGA_BIOS_DESCRIPTOR,
EXPECTED_SIZE,
)
}
}
fn translate_path(path: &Path) -> anyhow::Result<PathBuf> {
let file_path = if cfg!(windows) {
path.into()
} else if cfg!(target_os = "linux") {
let output = Command::new("wslpath")
.arg(path)
.output()
.context("Failed to translate path to windows. Are you not on WSL?")?;
String::from_utf8_lossy(&output.stdout).trim().into()
} else {
anyhow::bail!("No path specified for firmware and no default is configured for this OS.")
};
Ok(file_path)
}
fn parse_rom_file(
file_path: &Path,
firmware_name: &str,
descriptor: DllResourceDescriptor,
expected_len: usize,
) -> anyhow::Result<RomFileLocation> {
tracing::debug!(
?file_path,
?firmware_name,
"Attempting to load firmware file."
);
let file = fs_err::File::open(file_path)?;
let (start, len) = if let Some(maybe_resource) =
resource_dll_parser::try_find_resource_from_dll(&file, &descriptor)?
{
maybe_resource
} else {
(0, file.metadata()?.len() as usize)
};
if len != expected_len {
tracing::warn!(
firmware_name,
len,
expected_len,
"ROM data length does not match expected length, trying anyways",
);
}
Ok(RomFileLocation {
file: file.into(),
start,
len,
})
}
pub(crate) struct DllResourceDescriptor {
resource_type: [u8; 8],
id: u32,
}
impl DllResourceDescriptor {
const fn new(resource_type: &[u8; 4], id: u32) -> Self {
Self {
id,
resource_type: [
resource_type[0],
0,
resource_type[1],
0,
resource_type[2],
0,
resource_type[3],
0,
],
}
}
}