From 2d0f2fb8651de1507f5221d74a2ca68819b83f3c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 25 Feb 2023 17:46:00 +0100 Subject: [PATCH] main: iterate over regions of a Minecraft save dir --- src/main.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 63711c1..c10cd4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,42 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Parser; use minedmap::{resource, world}; #[derive(Debug, Parser)] struct Args { - /// Filename to dump - file: PathBuf, + /// Minecraft save directory + savedir: PathBuf, } -fn main() -> Result<()> { - let args = Args::parse(); +/// Type with methods for processing the regions of a Minecraft save directory +struct RegionProcessor { + block_types: resource::BlockTypeMap, +} - let block_types = resource::block_types(); +impl RegionProcessor { + fn new() -> Self { + RegionProcessor { + block_types: resource::block_types(), + } + } - minedmap::io::region::from_file(args.file.as_path())?.foreach_chunk( - |coords, data: world::de::Chunk| { + /// Parses a filename in the format r.X.Z.mca into the contained X and Z values + fn parse_region_filename(path: &Path) -> Option<(i32, i32)> { + let file_name = path.file_name()?.to_str()?; + let parts: Vec<_> = file_name.split('.').collect(); + let &["r", x, z, "mca"] = parts.as_slice() else { + return None; + }; + + Some((x.parse().ok()?, z.parse().ok()?)) + } + + /// Processes a single region file + fn process_region(&self, path: &Path, _x: i32, _z: i32) -> Result<()> { + minedmap::io::region::from_file(path)?.foreach_chunk(|coords, data: world::de::Chunk| { let chunk = match world::chunk::Chunk::new(&data) { Ok(chunk) => chunk, Err(err) => { @@ -26,10 +45,47 @@ fn main() -> Result<()> { } }; - match world::layer::top_layer(&chunk, &block_types) { + match world::layer::top_layer(&chunk, &self.block_types) { Ok(_) => {} Err(err) => println!("{:?}", err), } - }, - ) + }) + } + + /// Iterates over all region files of a Minecraft save directory + fn process_region_dir(&self, regiondir: &Path) -> Result<()> { + let read_dir = regiondir + .read_dir() + .with_context(|| format!("Failed to read directory {}", regiondir.display()))?; + + for entry in read_dir.filter_map(|entry| entry.ok()).filter(|entry| { + // We are only interested in regular files + entry + .file_type() + .map(|file_type| file_type.is_file()) + .unwrap_or_default() + }) { + let path = entry.path(); + let Some((x, z)) = Self::parse_region_filename(&path) else { + continue; + }; + + if let Err(err) = self.process_region(&path, x, z) { + eprintln!("Failed to process region r.{}.{}.mca: {}", x, z, err); + } + } + + Ok(()) + } +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let regiondir: PathBuf = [&args.savedir, Path::new("region")].iter().collect(); + + let region_processor = RegionProcessor::new(); + region_processor.process_region_dir(®iondir)?; + + Ok(()) }