diff --git a/Cargo.lock b/Cargo.lock index 3cf2b24..e1222a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "errno" version = "0.2.8" @@ -170,6 +176,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.139" @@ -190,6 +205,7 @@ dependencies = [ "clap", "fastnbt", "flate2", + "itertools", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index b704512..6ab5984 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,5 @@ anyhow = "1.0.68" clap = { version = "4.1.4", features = ["derive"] } fastnbt = "2.3.2" flate2 = "1.0.25" +itertools = "0.10.5" serde = "1.0.152" diff --git a/src/io/region.rs b/src/io/region.rs index ec42d3c..909e548 100644 --- a/src/io/region.rs +++ b/src/io/region.rs @@ -22,29 +22,19 @@ struct ChunkDesc { fn parse_header(header: &[u8; BLOCKSIZE]) -> HashMap { let mut map = HashMap::new(); - for z in 0..CHUNKS_PER_REGION { - for x in 0..CHUNKS_PER_REGION { - let chunk = - &header[(4 * (usize::from(CHUNKS_PER_REGION) * usize::from(z) + usize::from(x)))..]; + for coords in ChunkArray::<()>::keys() { + let chunk = &header[(4 + * (usize::from(CHUNKS_PER_REGION) * usize::from(coords.z.0) + + usize::from(coords.x.0)))..]; - let offset = u32::from(chunk[0]) << 16 | u32::from(chunk[1]) << 8 | u32::from(chunk[2]); - if offset == 0 { - continue; - } - - let len = chunk[3]; - - map.insert( - offset, - ChunkDesc { - coords: ChunkCoords { - x: ChunkX(x), - z: ChunkZ(z), - }, - len, - }, - ); + let offset = u32::from(chunk[0]) << 16 | u32::from(chunk[1]) << 8 | u32::from(chunk[2]); + if offset == 0 { + continue; } + + let len = chunk[3]; + + map.insert(offset, ChunkDesc { coords, len }); } map @@ -100,7 +90,7 @@ impl Region { }; let mut index = 1; - let mut seen = [[false; CHUNKS_PER_REGION as usize]; CHUNKS_PER_REGION as usize]; + let mut seen = ChunkArray::::default(); while !chunk_map.is_empty() { let Some(ChunkDesc { coords, len }) = chunk_map.remove(&index) else { @@ -109,11 +99,10 @@ impl Region { continue; }; - let chunk_seen = &mut seen[coords.x.0 as usize][coords.z.0 as usize]; - if *chunk_seen { + if seen[coords] { bail!("Duplicate chunk"); } - *chunk_seen = true; + seen[coords] = true; let mut buffer = vec![0; (len as usize) * BLOCKSIZE]; reader diff --git a/src/types.rs b/src/types.rs index 7fb8649..9aa4b7d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,9 @@ -use std::fmt::Debug; +use std::{ + fmt::Debug, + ops::{Index, IndexMut}, +}; + +use itertools::iproduct; pub const CHUNKS_PER_REGION: u8 = 32; @@ -22,3 +27,37 @@ impl Debug for ChunkCoords { write!(f, "({}, {})", self.x.0, self.z.0) } } + +#[derive(Debug, Clone, Copy, Default)] +pub struct ChunkArray(pub [[T; CHUNKS_PER_REGION as usize]; CHUNKS_PER_REGION as usize]); + +impl ChunkArray { + pub fn keys() -> impl Iterator { + iproduct!(0..CHUNKS_PER_REGION, 0..CHUNKS_PER_REGION).map(|(z, x)| ChunkCoords { + x: ChunkX(x), + z: ChunkZ(z), + }) + } + + pub fn values(&self) -> impl Iterator { + Self::keys().map(|k| &self[k]) + } + + pub fn iter(&self) -> impl Iterator { + Self::keys().map(|k| (k, &self[k])) + } +} + +impl Index for ChunkArray { + type Output = T; + + fn index(&self, index: ChunkCoords) -> &Self::Output { + &self.0[index.z.0 as usize][index.x.0 as usize] + } +} + +impl IndexMut for ChunkArray { + fn index_mut(&mut self, index: ChunkCoords) -> &mut Self::Output { + &mut self.0[index.z.0 as usize][index.x.0 as usize] + } +}