use crate::read::error::{Error, Result};
use crate::read::pointer::Pointer;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
const GVDB_SIGNATURE0: u32 = 1918981703;
const GVDB_SIGNATURE1: u32 = 1953390953;
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Immutable, KnownLayout, FromBytes, IntoBytes)]
pub struct Header {
signature: [u32; 2],
version: u32,
options: u32,
root: Pointer,
}
impl Header {
pub fn try_from_bytes(data: &[u8]) -> Result<Self> {
let (header, _) = Header::read_from_prefix(data)
.map_err(|_| Error::Data("Invalid GVDB header".to_string()))?;
if !header.header_valid() {
return Err(Error::Data(
"Invalid GVDB header. Is this a GVDB file?".to_string(),
));
}
if header.version() != 0 {
return Err(Error::Data(format!(
"Unknown GVDB file format version: {}",
header.version()
)));
}
Ok(header)
}
#[cfg(test)]
pub fn new_le(version: u32, root: Pointer) -> Self {
#[cfg(target_endian = "little")]
let byteswap = false;
#[cfg(target_endian = "big")]
let byteswap = true;
Self::new(byteswap, version, root)
}
#[cfg(test)]
pub fn new_be(version: u32, root: Pointer) -> Self {
#[cfg(target_endian = "little")]
let byteswap = true;
#[cfg(target_endian = "big")]
let byteswap = false;
Self::new(byteswap, version, root)
}
pub fn new(byteswap: bool, version: u32, root: Pointer) -> Self {
let signature = if !byteswap {
[GVDB_SIGNATURE0, GVDB_SIGNATURE1]
} else {
[GVDB_SIGNATURE0.swap_bytes(), GVDB_SIGNATURE1.swap_bytes()]
};
Self {
signature,
version: version.to_le(),
options: 0,
root,
}
}
pub fn is_byteswap(&self) -> Result<bool> {
if self.signature[0] == GVDB_SIGNATURE0 && self.signature[1] == GVDB_SIGNATURE1 {
Ok(false)
} else if self.signature[0] == GVDB_SIGNATURE0.swap_bytes()
&& self.signature[1] == GVDB_SIGNATURE1.swap_bytes()
{
Ok(true)
} else {
Err(Error::Data(format!(
"Invalid GVDB header signature: {:?}. Is this a GVariant database file?",
self.signature
)))
}
}
pub fn header_valid(&self) -> bool {
self.is_byteswap().is_ok()
}
pub fn version(&self) -> u32 {
self.version
}
pub fn root(&self) -> &Pointer {
&self.root
}
}
#[cfg(test)]
mod test {
use super::*;
use zerocopy::IntoBytes;
#[test]
fn derives() {
let header = Header::new(false, 0, Pointer::NULL);
let header2 = header;
println!("{header2:?}");
}
#[test]
fn header_serialize() {
let header = Header::new(false, 123, Pointer::NULL);
assert!(!header.is_byteswap().unwrap());
let data = header.as_bytes();
let parsed_header = Header::ref_from_bytes(data).unwrap();
assert!(!parsed_header.is_byteswap().unwrap());
let header = Header::new(true, 0, Pointer::NULL);
assert!(header.is_byteswap().unwrap());
let data = header.as_bytes();
let parsed_header = Header::ref_from_bytes(data).unwrap();
assert!(parsed_header.is_byteswap().unwrap());
}
}