use core::{fmt::Debug, str};
use std::io::{self, Cursor, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{header::DatabaseHeader, info::ExtraInfoRecord};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ExtraInfoCategory {
pub category_id: u8,
pub name: [u8; 16],
pub renamed: bool,
}
impl ExtraInfoCategory {
pub fn name_try_str<'x>(&'x self) -> Result<&'x str, str::Utf8Error> {
let mut idx = 0;
while idx < self.name.len() {
if self.name[idx] == 0u8 {
break;
}
idx += 1;
}
str::from_utf8(&self.name[0..idx])
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct AppInfoCategories {
pub renamed_categories: u16,
pub categories: Vec<ExtraInfoCategory>,
category_unique_ids: [u8; 16],
last_unique_id: u8,
rsvd: u8,
is_data: bool,
}
impl AppInfoCategories {
pub fn from_bytes(hdr: &DatabaseHeader, rdr: &mut Cursor<&[u8]>) -> Result<Self, io::Error> {
if &hdr.type_code[..] != b"DATA" {
return Ok(Default::default());
}
let mut categories = Vec::new();
let renamed_flags = rdr.read_u16::<BigEndian>()?;
for category_id in 0..16 {
let name = {
let mut buf = [0u8; 16];
rdr.read_exact(&mut buf)?;
buf
};
if name == [0u8; 16] {
continue;
}
categories.push(ExtraInfoCategory {
category_id,
name,
renamed: (renamed_flags & (1 << category_id)) != 0,
});
}
let mut category_unique_ids = [0_u8; 16];
for idx in 0..16 {
category_unique_ids[idx] = rdr.read_u8()?;
}
let last_unique_id = rdr.read_u8()?;
let rsvd = rdr.read_u8()?;
Ok(Self {
renamed_categories: renamed_flags,
categories,
category_unique_ids,
last_unique_id,
rsvd,
is_data: true,
})
}
}
impl ExtraInfoRecord for AppInfoCategories {
const SIZE: usize = 2 + 16 * 16 + 16 + 1 + 1;
fn from_bytes(hdr: &DatabaseHeader, data: &mut Cursor<&[u8]>) -> Result<Self, io::Error> {
Self::from_bytes(hdr, data)
}
fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
if !self.is_data {
return Ok(vec![]);
}
let mut cursor = Cursor::new(Vec::new());
cursor.write_u16::<BigEndian>(self.renamed_categories)?;
for cat in self.categories.iter() {
cursor.write(&cat.name)?;
}
for _ in self.categories.len()..16 {
cursor.write(&[0_u8; 16])?;
}
cursor.write(&self.category_unique_ids)?;
cursor.write_u8(self.last_unique_id)?;
cursor.write_u8(self.rsvd)?;
Ok(cursor.into_inner())
}
fn data_empty(&self) -> bool {
false
}
fn data_item_categories(&self) -> Option<Vec<ExtraInfoCategory>> {
Some(self.categories.clone())
}
}