Finish CLI
Dieser Commit ist enthalten in:
Ursprung
d352bfaded
Commit
0df001602e
@ -2,7 +2,15 @@
|
|||||||
members = [
|
members = [
|
||||||
"schemsearch-cli",
|
"schemsearch-cli",
|
||||||
"schemsearch-lib",
|
"schemsearch-lib",
|
||||||
|
"schemsearch_faster",
|
||||||
"schemsearch-files",
|
"schemsearch-files",
|
||||||
"schemsearch-sql",
|
"schemsearch-sql",
|
||||||
"schemsearch-java"
|
"schemsearch-java"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.test]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
@ -8,3 +8,4 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
schemsearch-lib = { path = "../schemsearch-lib" }
|
schemsearch-lib = { path = "../schemsearch-lib" }
|
||||||
schemsearch-files = { path = "../schemsearch-files" }
|
schemsearch-files = { path = "../schemsearch-files" }
|
||||||
|
clap = { version = "4.1.8", features = ["cargo"] }
|
||||||
|
@ -1,13 +1,203 @@
|
|||||||
use std::path::Path;
|
use std::fmt::format;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Stdout, StdoutLock, Write};
|
||||||
|
use clap::{command, Arg, ArgAction, ColorChoice, value_parser, Command};
|
||||||
use schemsearch_files::Schematic;
|
use schemsearch_files::Schematic;
|
||||||
use schemsearch_lib::pattern_mapper::match_palette;
|
use schemsearch_lib::pattern_mapper::match_palette;
|
||||||
|
use std::path::Path;
|
||||||
|
use clap::ArgAction::Help;
|
||||||
|
use clap::error::ErrorKind;
|
||||||
|
use schemsearch_lib::{search, SearchBehavior};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let schematic = Schematic::load(Path::new("tests/simple.schem"));
|
let mut cmd = command!("schemsearch")
|
||||||
let endstone = Schematic::load(Path::new("tests/endstone.schem"));
|
.arg(
|
||||||
|
Arg::new("pattern")
|
||||||
|
.help("The pattern to search for")
|
||||||
|
.required(true)
|
||||||
|
.action(ArgAction::Set),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("schematic")
|
||||||
|
.help("The schematics to search in")
|
||||||
|
.required(true)
|
||||||
|
.action(ArgAction::Append),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("ignore-data")
|
||||||
|
.help("Ignores block data when searching")
|
||||||
|
.short('d')
|
||||||
|
.long("ignore-data")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("ignore-block-entities")
|
||||||
|
.help("Ignores block entities when searching")
|
||||||
|
.short('b')
|
||||||
|
.long("ignore-block-entities")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("ignore-entities")
|
||||||
|
.help("Ignores entities when searching")
|
||||||
|
.short('e')
|
||||||
|
.long("ignore-entities")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("ignore-air")
|
||||||
|
.help("Ignores air when searching")
|
||||||
|
.short('a')
|
||||||
|
.long("ignore-air")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("air-as-any")
|
||||||
|
.help("Treats air as any block when searching")
|
||||||
|
.short('A')
|
||||||
|
.long("air-as-any")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("output")
|
||||||
|
.help("The output format")
|
||||||
|
.short('o')
|
||||||
|
.long("output")
|
||||||
|
.action(ArgAction::Append)
|
||||||
|
.default_value("std")
|
||||||
|
.value_parser(["std_csv", "file_csv", "std"]),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("output-file")
|
||||||
|
.help("The output file")
|
||||||
|
.short('O')
|
||||||
|
.long("output-file")
|
||||||
|
.action(ArgAction::Append)
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("threshold")
|
||||||
|
.help("The threshold for the search")
|
||||||
|
.short('t')
|
||||||
|
.long("threshold")
|
||||||
|
.action(ArgAction::Set)
|
||||||
|
.default_value("0.9")
|
||||||
|
.value_parser(|s: &str| s.parse::<f64>().map_err(|e| e.to_string())),
|
||||||
|
)
|
||||||
|
.about("Searches for a pattern in a schematic")
|
||||||
|
.bin_name("schemsearch");
|
||||||
|
|
||||||
let (matched_schematic, matched_endstone) = match_palette(&schematic, &endstone, true);
|
let matches = cmd.get_matches_mut();
|
||||||
|
|
||||||
println!("{:?}", matched_schematic);
|
if matches.contains_id("help") {
|
||||||
println!("{:?}", matched_endstone);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let search_behavior = SearchBehavior {
|
||||||
|
ignore_block_data: matches.get_flag("ignore-data"),
|
||||||
|
ignore_block_entities: matches.get_flag("ignore-block-entities"),
|
||||||
|
ignore_air: matches.get_flag("ignore-air"),
|
||||||
|
air_as_any: matches.get_flag("air-as-any"),
|
||||||
|
ignore_entities: matches.get_flag("ignore-entities"),
|
||||||
|
threshold: *matches.get_one::<f64>("threshold").expect("Couldn't get threshold"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pattern = match Schematic::load(Path::new(matches.get_one::<String>("pattern").unwrap())) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
cmd.error(ErrorKind::Io, format!("Error while loading Pattern: {}", e.to_string())).exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let schematics = matches.get_many::<String>("schematic").expect("Couldn't get schematics");
|
||||||
|
|
||||||
|
let mut output_std = false;
|
||||||
|
let mut output_std_csv = false;
|
||||||
|
let mut output_file_csv = false;
|
||||||
|
let mut output_file = false;
|
||||||
|
|
||||||
|
for x in matches.get_many::<String>("output").expect("Couldn't get output") {
|
||||||
|
match x.as_str() {
|
||||||
|
"std" => output_std = true,
|
||||||
|
"std_csv" => output_std_csv = true,
|
||||||
|
"file_csv" => output_file_csv = true,
|
||||||
|
"file" => output_file = true,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
let mut lock = stdout.lock();
|
||||||
|
|
||||||
|
let mut file: Option<File> = None;
|
||||||
|
let mut file_out: Option<BufWriter<File>> = None;
|
||||||
|
|
||||||
|
if output_file || output_file_csv {
|
||||||
|
let output_file_path = match matches.get_one::<String>("output-file") {
|
||||||
|
None => {
|
||||||
|
cmd.error(ErrorKind::MissingRequiredArgument, "No output file specified").exit();
|
||||||
|
}
|
||||||
|
Some(x) => x
|
||||||
|
};
|
||||||
|
|
||||||
|
file = match std::fs::File::create(output_file_path) {
|
||||||
|
Ok(x) => Some(x),
|
||||||
|
Err(e) => {
|
||||||
|
cmd.error(ErrorKind::Io, format!("Error while creating output file: {}", e.to_string())).exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
file_out = Some(BufWriter::new(file.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for schem_path in schematics {
|
||||||
|
let path = Path::new(schem_path);
|
||||||
|
if path.is_dir() {
|
||||||
|
match path.read_dir() {
|
||||||
|
Ok(x) => {
|
||||||
|
for path in x {
|
||||||
|
match path {
|
||||||
|
Ok(x) => {
|
||||||
|
if x.path().extension().unwrap_or_default() == "schem" {
|
||||||
|
search_schempath(&mut cmd, search_behavior, &pattern, &mut output_std, &mut output_std_csv, &mut output_file_csv, &mut output_file, &mut lock, &mut file_out, &x.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => cmd.error(ErrorKind::Io, format!("Error while reading dir: {}", e.to_string())).exit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => cmd.error(ErrorKind::Io, "Expected to be a dir").exit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
search_schempath(&mut cmd, search_behavior, &pattern, &mut output_std, &mut output_std_csv, &mut output_file_csv, &mut output_file, &mut lock, &mut file_out, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_schempath(cmd: &mut Command, search_behavior: SearchBehavior, pattern: &Schematic, output_std: &mut bool, output_std_csv: &mut bool, output_file_csv: &mut bool, output_file: &mut bool, stdout: &mut StdoutLock, file_out: &mut Option<BufWriter<File>>, schem_path: &Path) {
|
||||||
|
let schematic = match Schematic::load(schem_path) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
cmd.error(ErrorKind::Io, format!("Error while loading Schematic ({}): {}", schem_path.file_name().unwrap().to_str().unwrap(), e.to_string())).exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if *output_std {
|
||||||
|
writeln!(stdout, "Searching in schematic: {}", schem_path.file_name().unwrap().to_str().unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let matches = search(&schematic, &pattern, search_behavior);
|
||||||
|
|
||||||
|
for x in matches {
|
||||||
|
if *output_std {
|
||||||
|
writeln!(stdout, "Found match at x: {}, y: {}, z: {}", x.0, x.1, x.2).unwrap();
|
||||||
|
}
|
||||||
|
if *output_std_csv {
|
||||||
|
writeln!(stdout, "{},{},{},{}", schem_path.file_name().unwrap().to_str().unwrap(), x.0, x.1, x.2).unwrap();
|
||||||
|
}
|
||||||
|
if *output_file {
|
||||||
|
writeln!(file_out.as_mut().unwrap(), "Found match at x: {}, y: {}, z: {}", x.0, x.1, x.2).unwrap();
|
||||||
|
}
|
||||||
|
if *output_file_csv {
|
||||||
|
writeln!(file_out.as_mut().unwrap(), "{},{},{},{}", schem_path.file_name().unwrap().to_str().unwrap(), x.0, x.1, x.2).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use nbt::{Map, Value};
|
use nbt::{Map, Value};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Schematic {
|
pub struct Schematic {
|
||||||
@ -22,14 +23,22 @@ pub struct Schematic {
|
|||||||
pub palette_max: i32,
|
pub palette_max: i32,
|
||||||
#[serde(rename = "Palette")]
|
#[serde(rename = "Palette")]
|
||||||
pub palette: Map<String, i32>,
|
pub palette: Map<String, i32>,
|
||||||
#[serde(rename = "BlockData")]
|
#[serde(rename = "BlockData", deserialize_with = "read_blockdata")]
|
||||||
pub block_data: Vec<u8>,
|
pub block_data: Vec<i32>,
|
||||||
#[serde(rename = "BlockEntities")]
|
#[serde(rename = "BlockEntities")]
|
||||||
pub block_entities: Vec<BlockEntity>,
|
pub block_entities: Vec<BlockEntity>,
|
||||||
#[serde(rename = "Entities")]
|
#[serde(rename = "Entities")]
|
||||||
pub entities: Option<Vec<Entity>>,
|
pub entities: Option<Vec<Entity>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_blockdata<'de, D>(deserializer: D) -> Result<Vec<i32>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s: Vec<u8> = Deserialize::deserialize(deserializer)?;
|
||||||
|
Ok(read_varint_array(&s))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct BlockEntity {
|
pub struct BlockEntity {
|
||||||
#[serde(rename = "Id")]
|
#[serde(rename = "Id")]
|
||||||
@ -47,17 +56,16 @@ pub struct Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Schematic {
|
impl Schematic {
|
||||||
pub fn load(path: &Path) -> Schematic {
|
pub fn load(path: &Path) -> Result<Schematic, String> {
|
||||||
let file = std::fs::File::open(path).expect("Failed to open file");
|
let file = match std::fs::File::open(path) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => return Err(format!("Failed to open file: {}", path.display()))
|
||||||
|
};
|
||||||
let schematic: Schematic = match nbt::from_gzip_reader(file) {
|
let schematic: Schematic = match nbt::from_gzip_reader(file) {
|
||||||
Ok(schem) => schem,
|
Ok(schem) => schem,
|
||||||
Err(e) => panic!("Failed to parse schematic: {}", e),
|
Err(e) => return Err(format!("Failed to parse schematic: {}", e))
|
||||||
};
|
};
|
||||||
schematic
|
Ok(schematic)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_blockdata(&self) -> Vec<i32> {
|
|
||||||
read_varint_array(&self.block_data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,20 +97,3 @@ pub fn read_varint_array(read: &Vec<u8>) -> Vec<i32> {
|
|||||||
}
|
}
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_varint_array(data: &Vec<i32>) -> Vec<u8> {
|
|
||||||
let mut bytes: Vec<u8> = 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
|
|
||||||
}
|
|
@ -1,27 +1,29 @@
|
|||||||
#![no_mangle]
|
use std::path::Path;
|
||||||
|
|
||||||
use jni::JNIEnv;
|
use jni::JNIEnv;
|
||||||
|
|
||||||
use jni::objects::{JClass, JString};
|
use jni::objects::{JClass, JString};
|
||||||
|
|
||||||
use jni::sys::jstring;
|
use jni::sys::jstring;
|
||||||
|
use schemsearch_files::Schematic;
|
||||||
use schemsearch_lib::{search, SearchBehavior};
|
use schemsearch_lib::{search, SearchBehavior};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
pub extern "system" fn Java_SchemSearch_search<'local>(mut env: JNIEnv<'local>,
|
pub extern "system" fn Java_SchemSearch_search<'local>(mut env: JNIEnv<'local>,
|
||||||
class: JClass<'local>,
|
class: JClass<'local>,
|
||||||
schematic_path: JString<'local>,
|
schematic_path: JString<'local>,
|
||||||
pattern_path: JString<'local>) -> jstring {
|
pattern_path: JString<'local>) -> jstring {
|
||||||
let schematic_path: String = env.get_string(&schematic_path).expect("Couldn't get java string!").into();
|
let schematic_path: String = env.get_string(&schematic_path).expect("Couldn't get java string!").into();
|
||||||
let pattern_path: String = env.get_string(&pattern_path).expect("Couldn't get java string!").into();
|
let pattern_path: String = env.get_string(&pattern_path).expect("Couldn't get java string!").into();
|
||||||
let file = std::fs::File::open(schematic_path).expect("Failed to open file");
|
let schematic = Schematic::load(Path::new(&schematic_path));
|
||||||
let schematic = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect();
|
let pattern = Schematic::load(Path::new(&pattern_path));
|
||||||
let file = std::fs::File::open(pattern_path).expect("Failed to open file");
|
|
||||||
let pattern = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect();
|
|
||||||
|
|
||||||
let matches = search(schematic, pattern, SearchBehavior {
|
let matches = search(&schematic, &pattern, SearchBehavior {
|
||||||
ignore_block_data: true,
|
ignore_block_data: true,
|
||||||
ignore_block_entities: true,
|
ignore_block_entities: true,
|
||||||
ignore_entities: true,
|
ignore_entities: true,
|
||||||
|
ignore_air: false,
|
||||||
|
air_as_any: false,
|
||||||
|
threshold: 0.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
|
pub mod pattern_mapper;
|
||||||
|
|
||||||
use pattern_mapper::match_palette;
|
use pattern_mapper::match_palette;
|
||||||
use schemsearch_files::Schematic;
|
use schemsearch_files::Schematic;
|
||||||
|
|
||||||
pub mod pattern_mapper;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct SearchBehavior {
|
pub struct SearchBehavior {
|
||||||
pub ignore_block_data: bool,
|
pub ignore_block_data: bool,
|
||||||
pub ignore_block_entities: bool,
|
pub ignore_block_entities: bool,
|
||||||
|
pub ignore_air: bool,
|
||||||
|
pub air_as_any: bool,
|
||||||
pub ignore_entities: bool,
|
pub ignore_entities: bool,
|
||||||
|
pub threshold: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(
|
pub fn search(
|
||||||
data: &Vec<u8>,
|
schem: &Schematic,
|
||||||
pattern: &Vec<u8>,
|
pattern_schem: &Schematic,
|
||||||
search_behavior: SearchBehavior,
|
search_behavior: SearchBehavior,
|
||||||
) -> Vec<(u16, u16, u16)> {
|
) -> Vec<(u16, u16, u16)> {
|
||||||
let schem: Schematic = parse_schematic(data);
|
|
||||||
let pattern_schem: Schematic = parse_schematic(pattern);
|
|
||||||
|
|
||||||
if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
|
if schem.width < pattern_schem.width || schem.height < pattern_schem.height || schem.length < pattern_schem.length {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
@ -26,52 +26,44 @@ pub fn search(
|
|||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
let (schem, pattern_schem) = match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data);
|
let pattern_schem = match match_palette(&schem, &pattern_schem, search_behavior.ignore_block_data) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return vec![],
|
||||||
|
};
|
||||||
|
|
||||||
let mut matches: Vec<(u16, u16, u16)> = Vec::new();
|
let mut matches: Vec<(u16, u16, u16)> = Vec::new();
|
||||||
|
|
||||||
println!("{:?}", schem);
|
let pattern_data = pattern_schem.block_data;
|
||||||
println!("{:?}", pattern_schem);
|
let schem_data = &schem.block_data;
|
||||||
|
let air_id = if search_behavior.ignore_air || search_behavior.air_as_any { pattern_schem.palette.get("minecraft:air").unwrap_or(&-1) } else { &-1};
|
||||||
|
|
||||||
let pattern_data = pattern_schem.read_blockdata();
|
let pattern_blocks = (pattern_schem.width * pattern_schem.height * pattern_schem.length) as f64;
|
||||||
let schem_data = schem.read_blockdata();
|
|
||||||
|
|
||||||
for x in 0..=schem.width - pattern_schem.width {
|
for x in 0..=schem.width as usize - pattern_schem.width as usize {
|
||||||
for y in 0..=schem.height - pattern_schem.height {
|
for y in 0..=schem.height as usize - pattern_schem.height as usize {
|
||||||
for z in 0..=schem.length - pattern_schem.length {
|
for z in 0..=schem.length as usize - pattern_schem.length as usize {
|
||||||
let mut match_found = true;
|
let mut matching = 0;
|
||||||
'outer: for i in 0..pattern_schem.width {
|
for i in 0..pattern_schem.width as usize {
|
||||||
for j in 0..pattern_schem.height {
|
for j in 0..pattern_schem.height as usize {
|
||||||
for k in 0..pattern_schem.length {
|
for k in 0..pattern_schem.length as usize {
|
||||||
let index = (x + i) + (y + j) * schem.width + (z + k) * schem.width * schem.height;
|
let index = (x + i) + (y + j) * (schem.width as usize) + (z + k) * (schem.width as usize) * (schem.height as usize);
|
||||||
let pattern_index = i + j * pattern_schem.width + k * pattern_schem.width * pattern_schem.height;
|
let pattern_index = i + j * pattern_schem.width as usize + k * pattern_schem.width as usize * pattern_schem.height as usize;
|
||||||
if schem_data.get(index as usize) != pattern_data.get(pattern_index as usize) {
|
let data = schem_data.get(index as usize).expect("Index out of bounds");
|
||||||
match_found = false;
|
let pattern_data = pattern_data.get(pattern_index as usize).expect("Index out of bounds");
|
||||||
break 'outer;
|
if data == pattern_data || (search_behavior.ignore_air && *data == *air_id) || (search_behavior.air_as_any && *pattern_data == *air_id) {
|
||||||
|
matching += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if match_found {
|
if matching as f64 / pattern_blocks > search_behavior.threshold {
|
||||||
matches.push((x, y, z));
|
matches.push((x as u16, y as u16, z as u16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
[
|
|
||||||
0, -1, 1, 1, 2,
|
|
||||||
0, -1, 2, 1, 0,
|
|
||||||
2, -1, -1, 2, -1,
|
|
||||||
2, 0, 0, 2, -1,
|
|
||||||
2, 1, 2, 2, 1
|
|
||||||
]
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normalize_data(data: &String, ignore_data: bool) -> String {
|
pub fn normalize_data(data: &String, ignore_data: bool) -> String {
|
||||||
@ -104,7 +96,6 @@ mod tests {
|
|||||||
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.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);
|
assert_eq!(schematic.palette_max, schematic.palette.len() as i32);
|
||||||
println!("{:?}", schematic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -113,7 +104,6 @@ mod tests {
|
|||||||
let schematic: Schematic = parse_schematic(&std::io::Read::bytes(file).map(|b| b.unwrap()).collect());
|
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.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);
|
assert_eq!(schematic.palette_max, schematic.palette.len() as i32);
|
||||||
println!("{:?}", schematic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -122,7 +112,6 @@ mod tests {
|
|||||||
let stripped = strip_data(&schematic);
|
let stripped = strip_data(&schematic);
|
||||||
|
|
||||||
assert_eq!(stripped.palette.keys().any(|k| k.contains('[')), false);
|
assert_eq!(stripped.palette.keys().any(|k| k.contains('[')), false);
|
||||||
println!("{:?}", stripped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -130,17 +119,38 @@ mod tests {
|
|||||||
let schematic = Schematic::load(Path::new("../tests/simple.schem"));
|
let schematic = Schematic::load(Path::new("../tests/simple.schem"));
|
||||||
let endstone = Schematic::load(Path::new("../tests/endstone.schem"));
|
let endstone = Schematic::load(Path::new("../tests/endstone.schem"));
|
||||||
|
|
||||||
let (matched_schematic, matched_endstone) = match_palette(&schematic, &endstone, true);
|
let matched_schematic = match_palette(&schematic, &endstone, true);
|
||||||
|
}
|
||||||
|
|
||||||
println!("{:?}", matched_schematic);
|
#[test]
|
||||||
println!("{:?}", matched_endstone);
|
fn test_match_palette_ignore_data() {
|
||||||
|
let schematic = Schematic::load(Path::new("../tests/simple.schem"));
|
||||||
|
let endstone = Schematic::load(Path::new("../tests/endstone.schem"));
|
||||||
|
|
||||||
|
let matched_schematic = match_palette(&schematic, &endstone, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_big_search() {
|
||||||
|
let file = std::fs::File::open("../tests/simple.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/endstone.schem").expect("Failed to open file");
|
||||||
|
let pattern = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect();
|
||||||
|
|
||||||
|
let matches = search(schematic, pattern, SearchBehavior {
|
||||||
|
ignore_block_data: true,
|
||||||
|
ignore_block_entities: true,
|
||||||
|
ignore_entities: true,
|
||||||
|
ignore_air: false,
|
||||||
|
air_as_any: false,
|
||||||
|
threshold: 0.9
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn test_search() {
|
pub fn test_search() {
|
||||||
let file = std::fs::File::open("../tests/Random.schem").expect("Failed to open file");
|
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 schematic = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect();
|
||||||
println!("{:?}", schematic);
|
|
||||||
let file = std::fs::File::open("../tests/Pattern.schem").expect("Failed to open file");
|
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 pattern = &std::io::Read::bytes(file).map(|b| b.unwrap()).collect();
|
||||||
|
|
||||||
@ -148,6 +158,9 @@ mod tests {
|
|||||||
ignore_block_data: true,
|
ignore_block_data: true,
|
||||||
ignore_block_entities: true,
|
ignore_block_entities: true,
|
||||||
ignore_entities: true,
|
ignore_entities: true,
|
||||||
|
ignore_air: false,
|
||||||
|
air_as_any: false,
|
||||||
|
threshold: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
println!("{:?}", matches);
|
println!("{:?}", matches);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nbt::Map;
|
use nbt::Map;
|
||||||
use schemsearch_files::{Schematic, to_varint_array};
|
use schemsearch_files::Schematic;
|
||||||
use crate::normalize_data;
|
use crate::normalize_data;
|
||||||
|
|
||||||
fn create_reverse_palette(schem: &Schematic) -> Vec<String> {
|
fn create_reverse_palette(schem: &Schematic) -> Vec<String> {
|
||||||
@ -17,7 +17,7 @@ pub fn strip_data(schem: &Schematic) -> Schematic {
|
|||||||
let mut palette: Map<String, i32> = Map::new();
|
let mut palette: Map<String, i32> = Map::new();
|
||||||
let mut palette_max: i32 = 0;
|
let mut palette_max: i32 = 0;
|
||||||
let reverse_palette = create_reverse_palette(schem);
|
let reverse_palette = create_reverse_palette(schem);
|
||||||
let dat = schem.read_blockdata();
|
let dat = &schem.block_data;
|
||||||
|
|
||||||
for block in dat.iter() {
|
for block in dat.iter() {
|
||||||
let block_name = reverse_palette[*block as usize].clone();
|
let block_name = reverse_palette[*block as usize].clone();
|
||||||
@ -36,7 +36,7 @@ pub fn strip_data(schem: &Schematic) -> Schematic {
|
|||||||
data_version: schem.data_version,
|
data_version: schem.data_version,
|
||||||
palette,
|
palette,
|
||||||
palette_max,
|
palette_max,
|
||||||
block_data: to_varint_array(&data),
|
block_data: data,
|
||||||
block_entities: schem.block_entities.clone(),
|
block_entities: schem.block_entities.clone(),
|
||||||
height: schem.height,
|
height: schem.height,
|
||||||
length: schem.length,
|
length: schem.length,
|
||||||
@ -47,24 +47,27 @@ pub fn strip_data(schem: &Schematic) -> Schematic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_palette_adapt(schem: &Schematic, matching_palette: Map<String, i32>, ignore_data: bool) -> Vec<i32> {
|
fn match_palette_adapt(schem: &Schematic, matching_palette: &Map<String, i32>, ignore_data: bool) -> Option<Vec<i32>> {
|
||||||
let mut data: Vec<i32> = Vec::new();
|
let mut data: Vec<i32> = Vec::new();
|
||||||
|
|
||||||
for x in schem.read_blockdata().iter() {
|
for x in schem.block_data.iter() {
|
||||||
let blockname = schem.palette.iter().find(|(_, &v)| v == *x).expect("Invalid Schematic").0;
|
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 blockname = if ignore_data { normalize_data(&blockname, ignore_data) } else { blockname.clone() };
|
||||||
let block_id = matching_palette.get(&blockname).unwrap_or(&-1);
|
let block_id = match matching_palette.get(&blockname) {
|
||||||
|
None => return None,
|
||||||
|
Some(x) => x
|
||||||
|
};
|
||||||
data.push(*block_id);
|
data.push(*block_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
Some(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn match_palette(
|
pub fn match_palette(
|
||||||
schem: &Schematic,
|
schem: &Schematic,
|
||||||
pattern: &Schematic,
|
pattern: &Schematic,
|
||||||
ignore_data: bool,
|
ignore_data: bool,
|
||||||
) -> (Schematic, Schematic) {
|
) -> Option<Schematic> {
|
||||||
if ignore_data {
|
if ignore_data {
|
||||||
match_palette_internal(&strip_data(schem), &strip_data(pattern), ignore_data)
|
match_palette_internal(&strip_data(schem), &strip_data(pattern), ignore_data)
|
||||||
} else {
|
} else {
|
||||||
@ -76,46 +79,21 @@ fn match_palette_internal(
|
|||||||
schem: &Schematic,
|
schem: &Schematic,
|
||||||
pattern: &Schematic,
|
pattern: &Schematic,
|
||||||
ignore_data: bool,
|
ignore_data: bool,
|
||||||
) -> (Schematic, Schematic) {
|
) -> Option<Schematic> {
|
||||||
|
if pattern.palette.iter().any(|(k, _)| schem.palette.get(k).is_none()) {
|
||||||
if schem.palette.len() < pattern.palette.len() {
|
return None;
|
||||||
panic!("Schematic palette is larger than pattern palette");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut matching_palette: Map<String, i32> = Map::new();
|
let data_pattern: Vec<i32> = match match_palette_adapt(&pattern, &schem.palette, ignore_data) {
|
||||||
let mut matching_palette_max: i32 = 0;
|
None => return None,
|
||||||
|
Some(x) => x
|
||||||
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 data_schem: Vec<i32> = match_palette_adapt(&schem, matching_palette.clone(), true);
|
|
||||||
|
|
||||||
let data_pattern: Vec<i32> = 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: to_varint_array(&data_schem),
|
|
||||||
block_entities: schem.block_entities.clone(),
|
|
||||||
height: schem.height.clone(),
|
|
||||||
length: schem.length.clone(),
|
|
||||||
width: schem.width.clone(),
|
|
||||||
metadata: schem.metadata.clone(),
|
|
||||||
offset: schem.offset.clone(),
|
|
||||||
entities: None,
|
|
||||||
};
|
};
|
||||||
let pattern = Schematic {
|
Some(Schematic {
|
||||||
version: pattern.version.clone(),
|
version: pattern.version.clone(),
|
||||||
data_version: pattern.data_version.clone(),
|
data_version: pattern.data_version.clone(),
|
||||||
palette: matching_palette.clone(),
|
palette: schem.palette.clone(),
|
||||||
palette_max: matching_palette_max.clone(),
|
palette_max: schem.palette_max,
|
||||||
block_data: to_varint_array(&data_pattern),
|
block_data: data_pattern,
|
||||||
block_entities: pattern.block_entities.clone(),
|
block_entities: pattern.block_entities.clone(),
|
||||||
height: pattern.height.clone(),
|
height: pattern.height.clone(),
|
||||||
length: pattern.length.clone(),
|
length: pattern.length.clone(),
|
||||||
@ -123,7 +101,5 @@ fn match_palette_internal(
|
|||||||
metadata: pattern.metadata.clone(),
|
metadata: pattern.metadata.clone(),
|
||||||
offset: pattern.offset.clone(),
|
offset: pattern.offset.clone(),
|
||||||
entities: None,
|
entities: None,
|
||||||
};
|
})
|
||||||
|
|
||||||
(schem, pattern)
|
|
||||||
}
|
}
|
11
schemsearch_faster/Cargo.toml
Normale Datei
11
schemsearch_faster/Cargo.toml
Normale Datei
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "schemsearch_faster"
|
||||||
|
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" }
|
||||||
|
hematite-nbt = "0.5.2"
|
55
schemsearch_faster/src/lib.rs
Normale Datei
55
schemsearch_faster/src/lib.rs
Normale Datei
@ -0,0 +1,55 @@
|
|||||||
|
use nbt::Map;
|
||||||
|
use schemsearch_files::Schematic;
|
||||||
|
|
||||||
|
pub fn convert_to_search_space(schem: &Schematic, palette: &Vec<String>) -> Vec<Vec<u8>> {
|
||||||
|
let mut data: Vec<Vec<u8>> = Vec::with_capacity(palette.len());
|
||||||
|
let block_data = &schem.block_data;
|
||||||
|
for name in palette {
|
||||||
|
let mut output: Vec<u8> = Vec::with_capacity(block_data.len());
|
||||||
|
for block in block_data.iter() {
|
||||||
|
if schem.palette.get(name).unwrap_or(&-1) == block {
|
||||||
|
output.push(1);
|
||||||
|
} else {
|
||||||
|
output.push(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.push(output);
|
||||||
|
}
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_palette(palette: &Map<String, i32>) -> Vec<String> {
|
||||||
|
let mut output: Vec<String> = Vec::with_capacity(palette.len());
|
||||||
|
(0..palette.len()).for_each(|_| output.push(String::new()));
|
||||||
|
for (key, id) in palette.iter() {
|
||||||
|
output[*id as usize] = key.clone();
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
mod tests {
|
||||||
|
use std::path::Path;
|
||||||
|
use schemsearch_files::Schematic;
|
||||||
|
use crate::{convert_to_search_space, unwrap_palette};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test() {
|
||||||
|
let schematic = Schematic::load(Path::new("../tests/Pattern.schem"));
|
||||||
|
dbg!(convert_to_search_space(&schematic, &unwrap_palette(&schematic.palette)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_2() {
|
||||||
|
let schematic = Schematic::load(Path::new("../tests/Pattern.schem"));
|
||||||
|
let schematic2 = Schematic::load(Path::new("../tests/Random.schem"));
|
||||||
|
println!("{:?}", convert_to_search_space(&schematic2, &unwrap_palette(&schematic.palette)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_big() {
|
||||||
|
let schematic = Schematic::load(Path::new("../tests/endstone.schem"));
|
||||||
|
let schematic2 = Schematic::load(Path::new("../tests/simple.schem"));
|
||||||
|
let _ = convert_to_search_space(&schematic2, &unwrap_palette(&schematic.palette));
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren