use std::io::Cursor;
use winreg_core::hive::Hive;
#[derive(Debug, Clone, serde::Serialize)]
pub struct RegistryKeyInfo {
pub path: String,
pub name: String,
pub last_written: Option<String>,
pub subkey_count: usize,
pub value_count: usize,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct RegistryValueInfo {
pub key_path: String,
pub name: String,
pub data_type: String,
pub data_preview: String,
pub raw_size: usize,
}
pub fn walk_keys(hive: &Hive<Cursor<Vec<u8>>>) -> Vec<RegistryKeyInfo> {
let iter = match hive.iter_bfs() {
Ok(it) => it,
Err(_) => return Vec::new(),
};
let mut result = Vec::new();
for item in iter {
let key = match item {
Ok(k) => k,
Err(_) => continue,
};
let path = if key.is_root() {
String::new()
} else {
key.path().unwrap_or_default()
};
let last_written = key
.last_written()
.map(|dt| dt.format("%Y-%m-%dT%H:%M:%S").to_string());
result.push(RegistryKeyInfo {
name: key.name(),
path,
last_written,
subkey_count: key.subkey_count() as usize,
value_count: key.value_count() as usize,
});
}
result
}
pub fn walk_values(hive: &Hive<Cursor<Vec<u8>>>, key_path: &str) -> Vec<RegistryValueInfo> {
let key = match hive.open_key(key_path) {
Ok(Some(k)) => k,
_ => return Vec::new(),
};
let values = match key.values() {
Ok(v) => v,
Err(_) => return Vec::new(),
};
values
.into_iter()
.map(|v| value_to_info(v, key_path))
.collect()
}
pub fn walk_all_values(hive: &Hive<Cursor<Vec<u8>>>) -> Vec<RegistryValueInfo> {
let iter = match hive.iter_bfs() {
Ok(it) => it,
Err(_) => return Vec::new(),
};
let mut result = Vec::new();
for item in iter {
let key = match item {
Ok(k) => k,
Err(_) => continue,
};
let key_path = if key.is_root() {
String::new()
} else {
key.path().unwrap_or_default()
};
let values = match key.values() {
Ok(v) => v,
Err(_) => continue,
};
for v in values {
result.push(value_to_info(v, &key_path));
}
}
result
}
fn value_to_info(v: winreg_core::value::Value<'_>, key_path: &str) -> RegistryValueInfo {
let data_type = v.data_type().to_string();
let raw_size = v.data_size() as usize;
let data_preview = build_preview(&v);
RegistryValueInfo {
key_path: key_path.to_string(),
name: v.name(),
data_type,
data_preview,
raw_size,
}
}
fn build_preview(v: &winreg_core::value::Value<'_>) -> String {
use winreg_format::flags::ValueType;
match v.data_type() {
ValueType::Sz | ValueType::ExpandSz | ValueType::Link => {
let s = v.as_string().unwrap_or_default();
truncate_string(s, 256)
}
ValueType::Dword => {
let n = v.as_u32().unwrap_or(0);
format!("0x{n:08X} ({n})")
}
ValueType::DwordBigEndian => {
let n = v.as_u32_be().unwrap_or(0);
format!("0x{n:08X} ({n})")
}
ValueType::Qword => {
let n = v.as_u64().unwrap_or(0);
format!("0x{n:016X} ({n})")
}
ValueType::MultiSz => {
let parts = v.as_multi_string().unwrap_or_default();
let joined = parts.join(" | ");
truncate_string(joined, 256)
}
_ => {
let data = v.raw_data().unwrap_or_default();
let preview_len = data.len().min(32);
let hex: Vec<String> = data[..preview_len]
.iter()
.map(|b| format!("{b:02X}"))
.collect();
let s = hex.join(" ");
if data.len() > 32 {
format!("{s}...")
} else {
s
}
}
}
}
fn truncate_string(mut s: String, max_chars: usize) -> String {
if s.chars().count() > max_chars {
s = s.chars().take(max_chars).collect();
}
s
}