use binrw::{BinRead, BinWrite};
#[derive(Debug, Clone, Copy, BinRead, BinWrite)]
#[brw(little)]
pub struct McddChunk {
pub disable: [u8; 64],
}
impl Default for McddChunk {
fn default() -> Self {
Self { disable: [0; 64] }
}
}
impl McddChunk {
pub const SIZE_BYTES: usize = 64;
pub const GRID_SIZE: usize = 8;
#[must_use]
pub fn is_disabled(&self, x: usize, y: usize) -> bool {
if x >= Self::GRID_SIZE || y >= Self::GRID_SIZE {
return false;
}
let byte_index = y;
let bit_index = x;
let byte = self.disable[byte_index];
(byte & (1 << bit_index)) != 0
}
pub fn set_disabled(&mut self, x: usize, y: usize, disabled: bool) {
if x >= Self::GRID_SIZE || y >= Self::GRID_SIZE {
return;
}
let byte_index = y;
let bit_index = x;
if disabled {
self.disable[byte_index] |= 1 << bit_index;
} else {
self.disable[byte_index] &= !(1 << bit_index);
}
}
#[must_use]
pub fn disabled_count(&self) -> usize {
self.disable
.iter()
.map(|&byte| byte.count_ones() as usize)
.sum()
}
#[must_use]
pub fn all_enabled(&self) -> bool {
self.disable.iter().all(|&byte| byte == 0)
}
#[must_use]
pub fn all_disabled(&self) -> bool {
self.disable.iter().all(|&byte| byte == 0xFF)
}
}
#[cfg(test)]
mod tests {
use super::*;
use binrw::{BinReaderExt, BinWriterExt};
use std::io::Cursor;
#[test]
fn test_mcdd_size() {
assert_eq!(McddChunk::SIZE_BYTES, 64);
assert_eq!(McddChunk::GRID_SIZE, 8);
}
#[test]
fn test_mcdd_parse() {
let mut data = vec![0u8; 64];
data[0] = 0xFF; data[1] = 0x0F; data[7] = 0x80;
let mut cursor = Cursor::new(data);
let mcdd: McddChunk = cursor.read_le().unwrap();
assert_eq!(mcdd.disable[0], 0xFF);
assert_eq!(mcdd.disable[1], 0x0F);
assert_eq!(mcdd.disable[7], 0x80);
}
#[test]
fn test_mcdd_round_trip() {
let mut original = McddChunk::default();
original.disable[0] = 0xAA;
original.disable[7] = 0x55;
let mut buffer = Cursor::new(Vec::new());
buffer.write_le(&original).unwrap();
assert_eq!(buffer.position(), 64);
let data = buffer.into_inner();
let mut cursor = Cursor::new(data);
let parsed: McddChunk = cursor.read_le().unwrap();
assert_eq!(original.disable, parsed.disable);
}
#[test]
fn test_mcdd_is_disabled() {
let mut mcdd = McddChunk::default();
mcdd.disable[0] = 0b0000_0001; mcdd.disable[1] = 0b1000_0000; mcdd.disable[7] = 0b0101_0101;
assert!(mcdd.is_disabled(0, 0));
assert!(!mcdd.is_disabled(1, 0));
assert!(mcdd.is_disabled(7, 1));
assert!(!mcdd.is_disabled(0, 1));
assert!(mcdd.is_disabled(0, 7));
assert!(!mcdd.is_disabled(1, 7));
assert!(mcdd.is_disabled(2, 7));
assert!(!mcdd.is_disabled(3, 7));
assert!(!mcdd.is_disabled(8, 0));
assert!(!mcdd.is_disabled(0, 8));
}
#[test]
fn test_mcdd_set_disabled() {
let mut mcdd = McddChunk::default();
mcdd.set_disabled(0, 0, true);
assert!(mcdd.is_disabled(0, 0));
mcdd.set_disabled(7, 7, true);
assert!(mcdd.is_disabled(7, 7));
mcdd.set_disabled(0, 0, false);
assert!(!mcdd.is_disabled(0, 0));
mcdd.set_disabled(8, 0, true);
mcdd.set_disabled(0, 8, true);
}
#[test]
fn test_mcdd_disabled_count() {
let mut mcdd = McddChunk::default();
assert_eq!(mcdd.disabled_count(), 0);
mcdd.disable[0] = 0xFF; assert_eq!(mcdd.disabled_count(), 8);
mcdd.disable[1] = 0x0F; assert_eq!(mcdd.disabled_count(), 12);
mcdd.disable[7] = 0x80; assert_eq!(mcdd.disabled_count(), 13);
}
#[test]
fn test_mcdd_all_enabled() {
let mcdd1 = McddChunk::default();
assert!(mcdd1.all_enabled());
let mut mcdd2 = McddChunk::default();
mcdd2.disable[0] = 0x01;
assert!(!mcdd2.all_enabled());
}
#[test]
fn test_mcdd_all_disabled() {
let mut mcdd1 = McddChunk::default();
for i in 0..64 {
mcdd1.disable[i] = 0xFF;
}
assert!(mcdd1.all_disabled());
let mcdd2 = McddChunk::default();
assert!(!mcdd2.all_disabled());
}
#[test]
fn test_mcdd_default() {
let mcdd = McddChunk::default();
assert_eq!(mcdd.disable, [0u8; 64]);
assert_eq!(mcdd.disabled_count(), 0);
assert!(mcdd.all_enabled());
}
#[test]
fn test_mcdd_pattern() {
let mut mcdd = McddChunk::default();
for y in 0..8 {
for x in 0..8 {
if (x + y) % 2 == 0 {
mcdd.set_disabled(x, y, true);
}
}
}
assert_eq!(mcdd.disabled_count(), 32);
for y in 0..8 {
for x in 0..8 {
if (x + y) % 2 == 0 {
assert!(mcdd.is_disabled(x, y));
} else {
assert!(!mcdd.is_disabled(x, y));
}
}
}
}
}