From 26d83caa05fd0f116538e499a877c1db95222468 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sat, 4 Mar 2023 12:20:20 +0100 Subject: [PATCH] Restructure --- Cargo.toml | 16 +-- schemsearch-cli/Cargo.toml | 10 ++ schemsearch-cli/src/main.rs | 13 +++ schemsearch-files/Cargo.toml | 10 ++ schemsearch-files/src/lib.rs | 108 ++++++++++++++++++ schemsearch-lib/Cargo.toml | 11 ++ {src => schemsearch-lib/src}/lib.rs | 39 ++++--- .../src}/pattern_mapper.rs | 35 ++++-- src/schematic.rs | 58 ---------- 9 files changed, 202 insertions(+), 98 deletions(-) create mode 100644 schemsearch-cli/Cargo.toml create mode 100644 schemsearch-cli/src/main.rs create mode 100644 schemsearch-files/Cargo.toml create mode 100644 schemsearch-files/src/lib.rs create mode 100644 schemsearch-lib/Cargo.toml rename {src => schemsearch-lib/src}/lib.rs (77%) rename {src => schemsearch-lib/src}/pattern_mapper.rs (75%) delete mode 100644 src/schematic.rs diff --git a/Cargo.toml b/Cargo.toml index 03109d7..4c984d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,6 @@ -[package] -name = "schemsearch" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -hematite-nbt = "0.5.2" -serde = "1.0.152" +[workspace] +members = [ + "schemsearch-cli", + "schemsearch-lib", + "schemsearch-files" +] diff --git a/schemsearch-cli/Cargo.toml b/schemsearch-cli/Cargo.toml new file mode 100644 index 0000000..6a37a78 --- /dev/null +++ b/schemsearch-cli/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "schemsearch-cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +schemsearch-lib = { path = "../schemsearch-lib" } +schemsearch-files = { path = "../schemsearch-files" } \ No newline at end of file diff --git a/schemsearch-cli/src/main.rs b/schemsearch-cli/src/main.rs new file mode 100644 index 0000000..dae5457 --- /dev/null +++ b/schemsearch-cli/src/main.rs @@ -0,0 +1,13 @@ +use std::path::Path; +use schemsearch_files::Schematic; +use schemsearch_lib::pattern_mapper::match_palette; + +fn main() { + let schematic = Schematic::load(Path::new("tests/simple.schem")); + let endstone = Schematic::load(Path::new("tests/endstone.schem")); + + let (matched_schematic, matched_endstone) = match_palette(&schematic, &endstone, true); + + println!("{:?}", matched_schematic); + println!("{:?}", matched_endstone); +} diff --git a/schemsearch-files/Cargo.toml b/schemsearch-files/Cargo.toml new file mode 100644 index 0000000..f8dbb9b --- /dev/null +++ b/schemsearch-files/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "schemsearch-files" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hematite-nbt = "0.5.2" +serde = "1.0.152" diff --git a/schemsearch-files/src/lib.rs b/schemsearch-files/src/lib.rs new file mode 100644 index 0000000..9201edb --- /dev/null +++ b/schemsearch-files/src/lib.rs @@ -0,0 +1,108 @@ +use std::path::Path; +use nbt::{Map, Value}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Schematic { + #[serde(rename = "Version")] + pub version: i32, + #[serde(rename = "DataVersion")] + pub data_version: i32, + #[serde(rename = "Metadata")] + pub metadata: Map, + #[serde(rename = "Width")] + pub width: u16, + #[serde(rename = "Height")] + pub height: u16, + #[serde(rename = "Length")] + pub length: u16, + #[serde(rename = "Offset")] + pub offset: [i32; 3], + #[serde(rename = "PaletteMax")] + pub palette_max: i32, + #[serde(rename = "Palette")] + pub palette: Map, + #[serde(rename = "BlockData")] + pub block_data: Vec, + #[serde(rename = "BlockEntities")] + pub block_entities: Vec, + #[serde(rename = "Entities")] + pub entities: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BlockEntity { + #[serde(rename = "Id")] + pub id: String, + #[serde(rename = "Pos")] + pub pos: [i32; 3], +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Entity { + #[serde(rename = "Id")] + pub id: String, + #[serde(rename = "Pos")] + pub pos: [i32; 3], +} + +impl Schematic { + pub fn load(path: &Path) -> Schematic { + let file = std::fs::File::open(path).expect("Failed to open file"); + let schematic: Schematic = match nbt::from_gzip_reader(file) { + Ok(schem) => schem, + Err(e) => panic!("Failed to parse schematic: {}", e), + }; + schematic + } + + pub fn read_blockdata(&self) -> Vec { + read_varint_array(&self.block_data) + } +} + +pub fn read_varint_array(read: &Vec) -> Vec { + let mut data = Vec::new(); + let mut value: i32 = 0; + let mut position = 0; + let mut current_byte; + let mut cursor = 0; + loop { + match read.get(cursor) { + Some(byte) => { current_byte = *byte; cursor += 1; }, + None => break, + }; + + value |= (((current_byte & 0x7F) as u32) << position) as i32; + + if(current_byte & 0x80) == 0 { + data.push(value); + value = 0; + position = 0; + } else { + position += 7; + + if position > 32 { + panic!("VarInt too big"); + } + } + } + data +} + +pub fn to_varint_array(data: &Vec) -> Vec { + let mut bytes: Vec = Vec::new(); + for value in data { + let mut value = *value as u32; + 'inner: loop { + if (value & 0x80) == 0 { + bytes.push(value as u8); + break 'inner; + } + + bytes.push((value & 0x7F) as u8 | 0x80); + value >>= 7; + } + } + bytes +} \ No newline at end of file diff --git a/schemsearch-lib/Cargo.toml b/schemsearch-lib/Cargo.toml new file mode 100644 index 0000000..401a169 --- /dev/null +++ b/schemsearch-lib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "schemsearch-lib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hematite-nbt = "0.5.2" +serde = "1.0.152" +schemsearch-files = { path = "../schemsearch-files" } \ No newline at end of file diff --git a/src/lib.rs b/schemsearch-lib/src/lib.rs similarity index 77% rename from src/lib.rs rename to schemsearch-lib/src/lib.rs index 1c0bcfa..1533c73 100644 --- a/src/lib.rs +++ b/schemsearch-lib/src/lib.rs @@ -1,10 +1,7 @@ -#![allow(unused_variables)] +use pattern_mapper::match_palette; +use schemsearch_files::Schematic; -use crate::pattern_mapper::match_palette; -use crate::schematic::Schematic; - -mod schematic; -mod pattern_mapper; +pub mod pattern_mapper; #[derive(Debug, Clone, Copy)] pub struct SearchBehavior { @@ -36,6 +33,9 @@ pub fn search( println!("{:?}", schem); println!("{:?}", pattern_schem); + let pattern_data = pattern_schem.read_blockdata(); + let schem_data = schem.read_blockdata(); + for x in 0..=schem.width - pattern_schem.width { for y in 0..=schem.height - pattern_schem.height { for z in 0..=schem.length - pattern_schem.length { @@ -45,7 +45,7 @@ pub fn search( for k in 0..pattern_schem.length { let index = (x + i) + (y + j) * schem.width + (z + k) * schem.width * schem.height; let pattern_index = i + j * pattern_schem.width + k * pattern_schem.width * pattern_schem.height; - if schem.block_data.get(index as usize) != pattern_schem.block_data.get(pattern_index as usize) { + if schem_data.get(index as usize) != pattern_data.get(pattern_index as usize) { match_found = false; break 'outer; } @@ -74,7 +74,7 @@ pub fn search( } -pub(crate) fn normalize_data(data: &String, ignore_data: bool) -> String { +pub fn normalize_data(data: &String, ignore_data: bool) -> String { if ignore_data { data.split('[').next().unwrap().to_string() } else { @@ -82,7 +82,7 @@ pub(crate) fn normalize_data(data: &String, ignore_data: bool) -> String { } } -fn parse_schematic(data: &Vec) -> Schematic { +pub fn parse_schematic(data: &Vec) -> Schematic { if data[0] == 0x1f && data[1] == 0x8b { // gzip nbt::from_gzip_reader(data.as_slice()).unwrap() @@ -92,14 +92,16 @@ fn parse_schematic(data: &Vec) -> Schematic { } } +#[allow(unused_imports)] mod tests { use std::path::Path; - use crate::pattern_mapper::{match_palette, strip_data}; + use schemsearch_files::Schematic; + use crate::pattern_mapper::strip_data; use super::*; #[test] fn read_schematic() { - let schematic = Schematic::load(Path::new("tests/simple.schem")); + let schematic = Schematic::load(Path::new("../tests/simple.schem")); assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len()); assert_eq!(schematic.palette_max, schematic.palette.len() as i32); println!("{:?}", schematic); @@ -107,7 +109,7 @@ mod tests { #[test] fn test_parse_function() { - let file = std::fs::File::open("tests/simple.schem").expect("Failed to open file"); + let file = std::fs::File::open("../tests/simple.schem").expect("Failed to open file"); let schematic: Schematic = parse_schematic(&std::io::Read::bytes(file).map(|b| b.unwrap()).collect()); assert_eq!(schematic.width as usize * schematic.height as usize * schematic.length as usize, schematic.block_data.len()); assert_eq!(schematic.palette_max, schematic.palette.len() as i32); @@ -116,7 +118,7 @@ mod tests { #[test] fn test_strip_schem() { - let schematic = Schematic::load(Path::new("tests/simple.schem")); + let schematic = Schematic::load(Path::new("../tests/simple.schem")); let stripped = strip_data(&schematic); assert_eq!(stripped.palette.keys().any(|k| k.contains('[')), false); @@ -125,8 +127,8 @@ mod tests { #[test] fn test_match_palette() { - let schematic = Schematic::load(Path::new("tests/simple.schem")); - let endstone = Schematic::load(Path::new("tests/endstone.schem")); + let schematic = Schematic::load(Path::new("../tests/simple.schem")); + let endstone = Schematic::load(Path::new("../tests/endstone.schem")); let (matched_schematic, matched_endstone) = match_palette(&schematic, &endstone, true); @@ -135,10 +137,11 @@ mod tests { } #[test] - fn test_search() { - let file = std::fs::File::open("tests/Random.schem").expect("Failed to open file"); + pub fn test_search() { + let file = std::fs::File::open("../tests/Random.schem").expect("Failed to open file"); let schematic = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect(); - let file = std::fs::File::open("tests/Pattern.schem").expect("Failed to open file"); + println!("{:?}", schematic); + let file = std::fs::File::open("../tests/Pattern.schem").expect("Failed to open file"); let pattern = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect(); let matches = search(schematic, pattern, SearchBehavior { diff --git a/src/pattern_mapper.rs b/schemsearch-lib/src/pattern_mapper.rs similarity index 75% rename from src/pattern_mapper.rs rename to schemsearch-lib/src/pattern_mapper.rs index 8b3855a..4a06f3c 100644 --- a/src/pattern_mapper.rs +++ b/schemsearch-lib/src/pattern_mapper.rs @@ -1,15 +1,26 @@ use nbt::Map; +use schemsearch_files::{Schematic, to_varint_array}; use crate::normalize_data; -use crate::schematic::Schematic; -pub(crate) fn strip_data(schem: &Schematic) -> Schematic { +fn create_reverse_palette(schem: &Schematic) -> Vec { + let mut reverse_palette = Vec::with_capacity(schem.palette_max as usize); + (0..schem.palette_max).for_each(|_| reverse_palette.push(String::new())); + for (key, value) in schem.palette.iter() { + reverse_palette[*value as usize] = key.clone(); + } + reverse_palette +} + +pub fn strip_data(schem: &Schematic) -> Schematic { let mut data: Vec = Vec::new(); let mut palette: Map = Map::new(); let mut palette_max: i32 = 0; + let reverse_palette = create_reverse_palette(schem); + let dat = schem.read_blockdata(); - for block in schem.block_data.iter() { - let block_name = schem.palette.iter().find(|(_, &v)| v == *block).expect("Invalid Schematic").0; + for block in dat.iter() { + let block_name = reverse_palette[*block as usize].clone(); let block_name = block_name.split('[').next().unwrap().to_string(); let entry = palette.entry(block_name).or_insert_with(|| { @@ -25,7 +36,7 @@ pub(crate) fn strip_data(schem: &Schematic) -> Schematic { data_version: schem.data_version, palette, palette_max, - block_data: data, + block_data: to_varint_array(&data), block_entities: schem.block_entities.clone(), height: schem.height, length: schem.length, @@ -39,7 +50,7 @@ pub(crate) fn strip_data(schem: &Schematic) -> Schematic { fn match_palette_adapt(schem: &Schematic, matching_palette: Map, ignore_data: bool) -> Vec { let mut data: Vec = Vec::new(); - for x in schem.block_data.iter() { + for x in schem.read_blockdata().iter() { let blockname = schem.palette.iter().find(|(_, &v)| v == *x).expect("Invalid Schematic").0; let blockname = if ignore_data { normalize_data(&blockname, ignore_data) } else { blockname.clone() }; let block_id = matching_palette.get(&blockname).unwrap_or(&-1); @@ -49,7 +60,7 @@ fn match_palette_adapt(schem: &Schematic, matching_palette: Map, ig data } -pub(crate) fn match_palette( +pub fn match_palette( schem: &Schematic, pattern: &Schematic, ignore_data: bool, @@ -74,23 +85,23 @@ fn match_palette_internal( let mut matching_palette: Map = Map::new(); let mut matching_palette_max: i32 = 0; - for (block_name, block_id) in pattern.palette.iter() { + for (block_name, _) in pattern.palette.iter() { let block_name = normalize_data(block_name, true); let schem_block_id = pattern.palette.get(&block_name).expect("Pattern block not found in schematic palette"); matching_palette.insert(block_name, *schem_block_id); matching_palette_max += 1; } - let mut data_schem: Vec = match_palette_adapt(&schem, matching_palette.clone(), true); + let data_schem: Vec = match_palette_adapt(&schem, matching_palette.clone(), true); - let mut data_pattern: Vec = match_palette_adapt(&pattern, matching_palette.clone(), true); + let data_pattern: Vec = match_palette_adapt(&pattern, matching_palette.clone(), true); let schem = Schematic { version: schem.version.clone(), data_version: schem.data_version.clone(), palette: matching_palette.clone(), palette_max: matching_palette_max.clone(), - block_data: data_schem, + block_data: to_varint_array(&data_schem), block_entities: schem.block_entities.clone(), height: schem.height.clone(), length: schem.length.clone(), @@ -104,7 +115,7 @@ fn match_palette_internal( data_version: pattern.data_version.clone(), palette: matching_palette.clone(), palette_max: matching_palette_max.clone(), - block_data: data_pattern, + block_data: to_varint_array(&data_pattern), block_entities: pattern.block_entities.clone(), height: pattern.height.clone(), length: pattern.length.clone(), diff --git a/src/schematic.rs b/src/schematic.rs deleted file mode 100644 index 9a105ff..0000000 --- a/src/schematic.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::path::Path; -use nbt::{Map, Value}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug)] -pub(crate) struct Schematic { - #[serde(rename = "Version")] - pub(crate) version: i32, - #[serde(rename = "DataVersion")] - pub(crate) data_version: i32, - #[serde(rename = "Metadata")] - pub(crate) metadata: Map, - #[serde(rename = "Width")] - pub(crate) width: u16, - #[serde(rename = "Height")] - pub(crate) height: u16, - #[serde(rename = "Length")] - pub(crate) length: u16, - #[serde(rename = "Offset")] - pub(crate) offset: [i32; 3], - #[serde(rename = "PaletteMax")] - pub(crate) palette_max: i32, - #[serde(rename = "Palette")] - pub(crate) palette: Map, - #[serde(rename = "BlockData")] - pub(crate) block_data: Vec, - #[serde(rename = "BlockEntities")] - pub(crate) block_entities: Vec, - #[serde(rename = "Entities")] - pub(crate) entities: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub(crate) struct BlockEntity { - #[serde(rename = "Id")] - pub(crate) id: String, - #[serde(rename = "Pos")] - pub(crate) pos: [i32; 3], -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub(crate) struct Entity { - #[serde(rename = "Id")] - pub(crate) id: String, - #[serde(rename = "Pos")] - pub(crate) pos: [i32; 3], -} - -impl Schematic { - pub(crate) fn load(path: &Path) -> Schematic { - let file = std::fs::File::open(path).expect("Failed to open file"); - let schematic: Schematic = match nbt::from_gzip_reader(file) { - Ok(schem) => schem, - Err(e) => panic!("Failed to parse schematic: {}", e), - }; - schematic - } -}