mod base_block;
mod hive_bin;
mod cell;
mod key_node;
mod key_value;
mod key_security;
mod subkeys_list;
mod big_data;
mod data_types;
pub use base_block::*;
pub use hive_bin::*;
pub use cell::*;
pub use key_node::*;
pub use key_value::*;
pub use key_security::*;
pub use subkeys_list::*;
pub use big_data::*;
pub use data_types::*;
pub const REGF_SIGNATURE: &[u8; 4] = b"regf";
pub const HBIN_SIGNATURE: &[u8; 4] = b"hbin";
pub const BASE_BLOCK_SIZE: usize = 4096;
pub const MIN_HIVE_BIN_SIZE: usize = 4096;
pub const INVALID_OFFSET: u32 = 0xFFFFFFFF;
pub const BIG_DATA_THRESHOLD: usize = 16344;
pub const MAX_DATA_SEGMENT_SIZE: usize = 16344;
pub fn filetime_to_datetime(filetime: u64) -> Option<chrono::DateTime<chrono::Utc>> {
const FILETIME_UNIX_DIFF: u64 = 116444736000000000;
if filetime < FILETIME_UNIX_DIFF {
return None;
}
let unix_100ns = filetime - FILETIME_UNIX_DIFF;
let secs = (unix_100ns / 10_000_000) as i64;
let nsecs = ((unix_100ns % 10_000_000) * 100) as u32;
chrono::DateTime::from_timestamp(secs, nsecs)
}
pub fn datetime_to_filetime(dt: chrono::DateTime<chrono::Utc>) -> u64 {
const FILETIME_UNIX_DIFF: u64 = 116444736000000000;
let unix_100ns = (dt.timestamp() as u64) * 10_000_000
+ (dt.timestamp_subsec_nanos() as u64) / 100;
unix_100ns + FILETIME_UNIX_DIFF
}
pub fn calculate_checksum(data: &[u8]) -> u32 {
assert!(data.len() >= 508);
let mut checksum: u32 = 0;
for chunk in data[..508].chunks_exact(4) {
let value = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
checksum ^= value;
}
if checksum == 0xFFFFFFFF {
checksum = 0xFFFFFFFE;
} else if checksum == 0 {
checksum = 1;
}
checksum
}
pub fn marvin32_hash(data: &[u8], seed: u64) -> u64 {
let mut lo = seed as u32;
let mut hi = (seed >> 32) as u32;
let len = data.len();
let mut offset = 0;
while offset + 4 <= len {
let block = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
lo = lo.wrapping_add(block);
hi ^= lo;
lo = lo.rotate_left(20).wrapping_add(hi);
hi = hi.rotate_left(9) ^ lo;
lo = lo.rotate_left(27).wrapping_add(hi);
hi = hi.rotate_left(19);
offset += 4;
}
let remaining = len - offset;
let mut final_block: u32 = 0x80;
match remaining {
3 => {
final_block = (data[offset + 2] as u32) << 16
| (data[offset + 1] as u32) << 8
| (data[offset] as u32)
| 0x80000000;
}
2 => {
final_block = (data[offset + 1] as u32) << 8 | (data[offset] as u32) | 0x800000;
}
1 => {
final_block = (data[offset] as u32) | 0x8000;
}
0 => {}
_ => unreachable!(),
}
lo = lo.wrapping_add(final_block);
hi ^= lo;
lo = lo.rotate_left(20).wrapping_add(hi);
hi = hi.rotate_left(9) ^ lo;
lo = lo.rotate_left(27).wrapping_add(hi);
hi = hi.rotate_left(19);
lo = lo.wrapping_add(0);
hi ^= lo;
lo = lo.rotate_left(20).wrapping_add(hi);
hi = hi.rotate_left(9) ^ lo;
lo = lo.rotate_left(27).wrapping_add(hi);
hi = hi.rotate_left(19);
((hi as u64) << 32) | (lo as u64)
}
pub const MARVIN32_SEED: u64 = 0xC5554E7A884DEF82;
pub fn calculate_name_hash(name: &str) -> u32 {
let uppercase = name.to_uppercase();
let mut hash: u32 = 0;
for c in uppercase.chars() {
let code = c as u32;
hash = hash.wrapping_mul(37).wrapping_add(code);
}
hash
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checksum() {
let mut data = vec![0u8; 512];
for i in 0..127 {
let offset = i * 4;
data[offset..offset + 4].copy_from_slice(&(i as u32).to_le_bytes());
}
let checksum = calculate_checksum(&data);
assert_ne!(checksum, 0);
}
#[test]
fn test_name_hash() {
let hash = calculate_name_hash("Test");
assert!(hash != 0);
assert_eq!(calculate_name_hash("test"), calculate_name_hash("TEST"));
}
#[test]
fn test_filetime_conversion() {
use chrono::{TimeZone, Utc};
let dt = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
let filetime = datetime_to_filetime(dt);
let converted = filetime_to_datetime(filetime).unwrap();
assert_eq!(dt, converted);
}
}