///|
/// Size in bytes of the REGF base block that starts every hive file.
let base_block_size : Int32 = 4096
///|
/// Kind of file recorded in the base block.
pub(all) enum HiveFileType {
Primary
Log
External
Unknown(UInt32)
}
///|
/// Short label for a hive file type.
pub fn HiveFileType::label(self : HiveFileType) -> String {
match self {
Primary => "primary"
Log => "log"
External => "external"
Unknown(value) => "unknown(" + value.to_string() + ")"
}
}
///|
/// Parsed REGF base block (the 4096 bytes at the start of a hive file).
pub(all) struct HiveHeader {
primary_sequence : UInt32
secondary_sequence : UInt32
last_written_high : UInt32
last_written_low : UInt32
major_version : UInt32
minor_version : UInt32
file_type : HiveFileType
file_format : UInt32
root_cell_offset : UInt32
hive_bins_data_size : UInt32
clustering_factor : UInt32
file_name : String
checksum : UInt32
}
///|
/// True when the first four bytes are the ASCII bytes of regf.
///
/// Bytes are compared directly instead of being decoded into a string: string
/// decoding depends on the backend and must not be used for format checks.
fn signature_matches(bytes : Bytes) -> Bool {
bytes[0].to_int() == 0x72 &&
bytes[1].to_int() == 0x65 &&
bytes[2].to_int() == 0x67 &&
bytes[3].to_int() == 0x66
}
///|
/// Renders the first four bytes as lowercase hex, for diagnostics.
fn signature_hex(bytes : Bytes) -> String {
"0x" +
bytes[0].to_uint().to_string(radix=16) +
bytes[1].to_uint().to_string(radix=16) +
bytes[2].to_uint().to_string(radix=16) +
bytes[3].to_uint().to_string(radix=16)
}
///|
/// XOR checksum over the first 508 bytes of the base block.
fn compute_checksum(bytes : Bytes) -> UInt32 {
let mut acc : UInt32 = 0
let mut offset = 0
while offset < 508 {
acc = acc ^ read_u32_le(bytes, offset)
offset = offset + 4
}
acc
}
///|
/// Parses the base block of a hive file.
pub fn parse_header(bytes : Bytes) -> Result[HiveHeader, RegfError] {
let size = bytes.length()
if size < base_block_size {
return Err(TooShort(size))
}
if !signature_matches(bytes) {
return Err(BadSignature(signature_hex(bytes)))
}
let computed = compute_checksum(bytes)
let stored = read_u32_le(bytes, 0x1FC)
if computed != stored {
return Err(BadChecksum(stored, computed))
}
let raw_type = read_u32_le(bytes, 0x1C)
let file_type = if raw_type == 0 {
Primary
} else if raw_type == 1 {
Log
} else if raw_type == 2 {
External
} else {
Unknown(raw_type)
}
let parsed : HiveHeader = {
primary_sequence: read_u32_le(bytes, 0x04),
secondary_sequence: read_u32_le(bytes, 0x08),
last_written_high: read_u32_le(bytes, 0x10),
last_written_low: read_u32_le(bytes, 0x0C),
major_version: read_u32_le(bytes, 0x14),
minor_version: read_u32_le(bytes, 0x18),
file_type,
file_format: read_u32_le(bytes, 0x20),
root_cell_offset: read_u32_le(bytes, 0x24),
hive_bins_data_size: read_u32_le(bytes, 0x28),
clustering_factor: read_u32_le(bytes, 0x2C),
file_name: decode_utf16le(bytes, 0x30, 32),
checksum: stored,
}
Ok(parsed)
}