use regf::reg_export::RegParser;
use regf::reg_import::RegImporter;
use regf::writer::HiveBuilder;
use regf::hive::RegistryHive;
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== .reg Text to Hive Conversion Examples ===\n");
println!("--- Example 0: Simple Import using RegImporter ---");
let simple_reg = r#"Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Example]
"Name"="Simple Example"
"Value"=dword:00000001
"#;
let importer = RegImporter::from_string(simple_reg);
let hive_bytes = importer.build_hive()?;
let hive = RegistryHive::from_bytes(hive_bytes)?;
println!("Created hive with root: {}", hive.root_key()?.name());
println!("This is the recommended way for simple conversions!\n");
println!("--- Example 1: Low-level .reg Parsing ---");
let reg_content = r#"Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\MyCompany]
"CompanyName"="Acme Corporation"
"Founded"=dword:000007d0
@="Default Company Value"
[HKEY_LOCAL_MACHINE\SOFTWARE\MyCompany\Products]
"Product1"="Widget Pro"
"Product2"="Gadget Plus"
"Price"=dword:00000063
[HKEY_LOCAL_MACHINE\SOFTWARE\MyCompany\Settings]
"Enabled"=dword:00000001
"ConfigPath"=hex(2):25,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,44,00,61,00,74,00,61,00,25,00,5c,00,4d,00,79,00,43,00,6f,00,6d,00,70,00,61,00,6e,00,79,00,00,00
"#;
let parser = RegParser::new(reg_content.to_string());
let entries = parser.parse();
println!("Parsed {} key(s):", entries.len());
for entry in &entries {
println!("\n Key: {}", entry.key_path);
for value in &entry.values {
let name = if value.name.is_empty() { "(Default)" } else { &value.name };
println!(" {} ({:?}) = {} bytes", name, value.data_type, value.data.len());
}
}
println!("\n--- Example 2: Convert .reg to Binary Hive ---");
let hive = reg_to_hive(reg_content)?;
println!("\nCreated hive with keys:");
for path in hive.enumerate_all_keys()? {
println!(" {}", path);
}
println!("\n--- Example 3: Complex .reg File ---");
let complex_reg = r#"Windows Registry Editor Version 5.00
; This is a comment - should be ignored
[HKEY_CURRENT_USER\Software\TestApp]
"Name"="Test Application"
"Version"="2.0.0"
"InstallDate"=hex(b):00,80,3e,d5,de,b1,9d,01
"Features"=hex(7):46,00,65,00,61,00,74,00,75,00,72,00,65,00,31,00,00,00,46,00,65,00,61,00,74,00,75,00,72,00,65,00,32,00,00,00,00,00
[HKEY_CURRENT_USER\Software\TestApp\Window]
"Width"=dword:00000320
"Height"=dword:00000258
"Maximized"=dword:00000000
"Position"=hex:64,00,00,00,64,00,00,00
[HKEY_CURRENT_USER\Software\TestApp\RecentFiles]
"File1"="C:\\Documents\\report.txt"
"File2"="C:\\Documents\\notes.txt"
"File3"="C:\\Documents\\data.csv"
"#;
let hive = reg_to_hive(complex_reg)?;
println!("Reading values from converted hive:");
let app = hive.open_key("Software\\TestApp")?;
for value in app.values()? {
let name = if value.is_default() { "(Default)" } else { &value.name() };
match value.data()? {
regf::structures::RegistryValue::String(s) => println!(" {} = \"{}\"", name, s),
regf::structures::RegistryValue::Dword(d) => println!(" {} = 0x{:08X}", name, d),
regf::structures::RegistryValue::Qword(q) => println!(" {} = 0x{:016X}", name, q),
regf::structures::RegistryValue::MultiString(ms) => println!(" {} = {:?}", name, ms),
regf::structures::RegistryValue::Binary(b) => println!(" {} = {:02X?}", name, b),
_ => println!(" {} = (other)", name),
}
}
println!("\n--- Example 4: Save to Disk ---");
let output_path = std::env::temp_dir().join("converted.dat");
let mut builder = build_hive_from_reg(complex_reg)?;
builder.write_to_file(&output_path)?;
println!("Saved hive to: {:?}", output_path);
let loaded = RegistryHive::from_file(&output_path)?;
println!("Loaded hive has {} keys", loaded.enumerate_all_keys()?.len());
std::fs::remove_file(&output_path)?;
println!("\n--- Example 5: Round-Trip Conversion ---");
let original_reg = r#"Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\RoundTrip]
"StringValue"="Hello World"
"DwordValue"=dword:0000002a
"BinaryValue"=hex:01,02,03,04,05
"#;
let hive = reg_to_hive(original_reg)?;
use regf::reg_export::{RegExporter, RegExportOptions, RegVersion};
let options = RegExportOptions {
version: RegVersion::Version5,
root_path: "HKEY_LOCAL_MACHINE".to_string(),
include_empty_keys: false,
recursive: true,
};
let exporter = RegExporter::new(&hive, options);
let mut output = Vec::new();
exporter.export(&mut output)?;
println!("Original .reg:");
println!("{}", original_reg);
println!("After round-trip:");
print_reg_content(&output);
println!("\nAll examples completed!");
Ok(())
}
fn reg_to_hive(reg_content: &str) -> Result<RegistryHive, Box<dyn std::error::Error>> {
let mut builder = build_hive_from_reg(reg_content)?;
let bytes = builder.to_bytes()?;
Ok(RegistryHive::from_bytes(bytes)?)
}
fn build_hive_from_reg(reg_content: &str) -> Result<HiveBuilder, Box<dyn std::error::Error>> {
let parser = RegParser::new(reg_content.to_string());
let entries = parser.parse();
let mut builder = HiveBuilder::new();
let root = builder.root_offset();
let mut key_offsets: HashMap<String, u32> = HashMap::new();
key_offsets.insert(String::new(), root);
for entry in entries {
let path = strip_hive_prefix(&entry.key_path);
let key_offset = ensure_key_path(&mut builder, &mut key_offsets, &path)?;
for value in entry.values {
builder.add_value(
key_offset,
&value.name,
value.data_type,
&value.data,
)?;
}
}
Ok(builder)
}
fn strip_hive_prefix(path: &str) -> String {
let prefixes = [
"HKEY_LOCAL_MACHINE\\",
"HKEY_CURRENT_USER\\",
"HKEY_CLASSES_ROOT\\",
"HKEY_USERS\\",
"HKEY_CURRENT_CONFIG\\",
"HKLM\\",
"HKCU\\",
"HKCR\\",
"HKU\\",
"HKCC\\",
];
for prefix in prefixes {
if let Some(rest) = path.strip_prefix(prefix) {
return rest.to_string();
}
}
let upper = path.to_uppercase();
for prefix in prefixes {
if let Some(idx) = upper.find(&prefix.to_uppercase()) {
if idx == 0 {
return path[prefix.len()..].to_string();
}
}
}
path.to_string()
}
fn ensure_key_path(
builder: &mut HiveBuilder,
offsets: &mut HashMap<String, u32>,
path: &str,
) -> Result<u32, Box<dyn std::error::Error>> {
if path.is_empty() {
return Ok(*offsets.get("").unwrap());
}
if let Some(&offset) = offsets.get(path) {
return Ok(offset);
}
let parts: Vec<&str> = path.split('\\').collect();
let parent_path = if parts.len() > 1 {
parts[..parts.len() - 1].join("\\")
} else {
String::new()
};
let parent_offset = ensure_key_path(builder, offsets, &parent_path)?;
let key_name = parts.last().unwrap();
let key_offset = builder.add_key(parent_offset, key_name)?;
offsets.insert(path.to_string(), key_offset);
Ok(key_offset)
}
fn print_reg_content(data: &[u8]) {
if data.len() >= 2 && data[0] == 0xFF && data[1] == 0xFE {
let u16_values: Vec<u16> = data[2..]
.chunks(2)
.filter_map(|c| {
if c.len() == 2 {
Some(u16::from_le_bytes([c[0], c[1]]))
} else {
None
}
})
.collect();
println!("{}", String::from_utf16_lossy(&u16_values));
} else {
println!("{}", String::from_utf8_lossy(data));
}
}