From 6fd1293e4aab31a3ff06230f2b23a8008186583f Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 25 Sep 2020 19:21:11 +0200 Subject: [PATCH] Add subcommand to get the number of chunks inside a world folder Signed-off-by: trivernis --- .gitignore | 3 + Cargo.lock | 328 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++ src/lib.rs | 2 + src/main.rs | 28 ++++ src/region_file.rs | 91 ++++++++++++ src/world_folder.rs | 33 +++++ 7 files changed, 498 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/region_file.rs create mode 100644 src/world_folder.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f6ad5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +.idea +testdata \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..82df4b4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,328 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" + +[[package]] +name = "lock_api" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "minecraft-regions-tool" +version = "0.1.0" +dependencies = [ + "byteorder", + "num_cpus", + "scheduled-thread-pool", + "structopt", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +dependencies = [ + "cfg-if", + "cloudabi", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f9a4f2a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "minecraft-regions-tool" +version = "0.1.0" +authors = ["trivernis "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +byteorder = "1.3.4" +structopt = "0.3.18" +scheduled-thread-pool = "0.2.5" +num_cpus = "1.13.0" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..10883dc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod region_file; +pub mod world_folder; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8d3af3d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,28 @@ +use minecraft_regions_tool::world_folder::WorldFolder; +use std::path::PathBuf; +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct Opt { + /// Path to the world folder + #[structopt(parse(from_os_str))] + input: PathBuf, + + #[structopt(subcommand)] + sub_command: SubCommand, +} + +#[derive(StructOpt, Debug)] +#[structopt()] +enum SubCommand { + /// Return the total number of chunks in the world + Count, +} + +fn main() { + let opt: Opt = Opt::from_args(); + let world = WorldFolder::new(opt.input); + match opt.sub_command { + SubCommand::Count => println!("Chunk Count: {}", world.count_chunks().unwrap()), + } +} diff --git a/src/region_file.rs b/src/region_file.rs new file mode 100644 index 0000000..e473668 --- /dev/null +++ b/src/region_file.rs @@ -0,0 +1,91 @@ +use byteorder::{BigEndian, ByteOrder}; +use std::io::{Read, Result}; + +const BLOCK_SIZE: usize = 4096; + +pub struct RegionFile { + reader: Box, + locations: Locations, + timestamps: Timestamps, +} + +impl RegionFile { + pub fn new(reader: Box) -> Result { + let mut locations_raw = [0u8; BLOCK_SIZE]; + let mut timestamps_raw = [0u8; BLOCK_SIZE]; + let mut reader = reader; + reader.read_exact(&mut locations_raw)?; + reader.read_exact(&mut timestamps_raw)?; + + Ok(Self { + locations: Locations::from_bytes(&locations_raw), + timestamps: Timestamps::from_bytes(×tamps_raw), + reader, + }) + } + + /// Returns the number of chunks in the file + pub fn count_chunks(&self) -> usize { + let mut count = 0; + for x in 0..32 { + for z in 0..32 { + if !(self.locations.get_chunk_offset(x, z) == Some(0) + && self.locations.get_chunk_sectors(x, z) == Some(0)) + { + count += 1; + } + } + } + + return count; + } +} + +#[derive(Debug)] +pub struct Locations { + inner: Vec<(u32, u8)>, +} + +impl Locations { + pub fn from_bytes(bytes: &[u8; BLOCK_SIZE]) -> Self { + let mut locations = Vec::new(); + + for i in (0..BLOCK_SIZE - 1).step_by(4) { + let mut offset = BigEndian::read_u32(&bytes[i..i + 4]); + offset = offset >> 1; + let count = bytes[i + 3]; + locations.push((offset, count)); + } + + Self { inner: locations } + } + + /// Returns the offset of a chunk + pub fn get_chunk_offset(&self, x: usize, z: usize) -> Option { + let index = x % 32 + (z % 32) * 32; + self.inner.get(index).map(|e| (*e).0) + } + + /// Returns the number of sectors for a chunk + pub fn get_chunk_sectors(&self, x: usize, z: usize) -> Option { + let index = x % 32 + (z % 32) * 32; + self.inner.get(index).map(|e| (*e).1) + } +} + +#[derive(Debug)] +pub struct Timestamps { + inner: Vec, +} + +impl Timestamps { + pub fn from_bytes(bytes: &[u8; BLOCK_SIZE]) -> Self { + let mut timestamps = Vec::new(); + + for i in (0..BLOCK_SIZE - 1).step_by(4) { + timestamps.push(BigEndian::read_u32(&bytes[i..i + 4])) + } + + Self { inner: timestamps } + } +} diff --git a/src/world_folder.rs b/src/world_folder.rs new file mode 100644 index 0000000..bdbf964 --- /dev/null +++ b/src/world_folder.rs @@ -0,0 +1,33 @@ +use crate::region_file::RegionFile; +use std::ffi::OsStr; +use std::fs; +use std::fs::File; +use std::io; +use std::io::BufReader; +use std::path::PathBuf; + +pub struct WorldFolder { + path: PathBuf, +} + +impl WorldFolder { + pub fn new(path: PathBuf) -> Self { + Self { path } + } + + pub fn count_chunks(&self) -> io::Result { + let mut count = 0u64; + let region_file_path = self.path.join(PathBuf::from("region")); + + for file in fs::read_dir(region_file_path)? { + let file_path = file?.path(); + if file_path.extension() == Some(OsStr::new("mca")) { + let f = File::open(file_path)?; + let region_file = RegionFile::new(Box::new(BufReader::new(f)))?; + count += region_file.count_chunks() as u64; + } + } + + Ok(count) + } +}