diff --git a/schemsearch-files/Cargo.toml b/schemsearch-files/Cargo.toml index cfaaff8..76d6ddf 100644 --- a/schemsearch-files/Cargo.toml +++ b/schemsearch-files/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] flate2 = "1.0.25" -fastnbt = "2.4.3" +hematite-nbt = "0.5.2" serde = "1.0.152" serde_json = "1.0.84" diff --git a/schemsearch-files/src/lib.rs b/schemsearch-files/src/lib.rs index 502a1e4..0fe2838 100644 --- a/schemsearch-files/src/lib.rs +++ b/schemsearch-files/src/lib.rs @@ -15,25 +15,12 @@ * along with this program. If not, see . */ -use std::collections::hash_map::{HashMap}; +use std::collections::hash_map::HashMap; use std::io::Read; use std::path::PathBuf; -use fastnbt::error::Error; -use fastnbt::Value; -use flate2::read::GzDecoder; -use serde::{Deserialize, Deserializer, Serialize}; -use serde::de::value::MapDeserializer; +use nbt::Value; -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -struct SchematicRaw { - version: i32, - #[serde(flatten)] - data: HashMap, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(untagged, rename_all = "PascalCase")] +#[derive(Clone, Debug)] pub enum SchematicVersioned { V1(SpongeV1Schematic), V2(SpongeV2Schematic), @@ -105,30 +92,7 @@ impl SchematicVersioned { } } -impl TryFrom for SchematicVersioned { - type Error = Error; - - fn try_from(value: SchematicRaw) -> Result { - match value.version { - 1 => { - let schematic: SpongeV1Schematic = fastnbt::from_value(&fastnbt::to_value(value.data)?)?; - return Ok(SchematicVersioned::V1(schematic)); - }, - 2 => { - let schematic: SpongeV2Schematic = fastnbt::from_value(&fastnbt::to_value(value.data)?)?; - return Ok(SchematicVersioned::V2(schematic)); - }, - 3 => { - let schematic: SpongeV3Schematic = fastnbt::from_value(&fastnbt::to_value(value.data)?)?; - return Ok(SchematicVersioned::V3(schematic)); - } - _ => panic!("Unknown Schematic Version: {}", value.version), - } - } -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] +#[derive(Clone, Debug)] pub struct SpongeV1Schematic { pub metadata: HashMap, pub width: u16, @@ -137,13 +101,11 @@ pub struct SpongeV1Schematic { pub offset: [i32; 3], pub palette_max: i32, pub palette: HashMap, - #[serde(deserialize_with = "read_blockdata")] pub block_data: Vec, pub tile_entities: Vec, } -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] +#[derive(Clone, Debug)] pub struct SpongeV2Schematic { pub data_version: i32, pub metadata: HashMap, @@ -153,14 +115,12 @@ pub struct SpongeV2Schematic { pub offset: [i32; 3], pub palette_max: i32, pub palette: HashMap, - #[serde(deserialize_with = "read_blockdata")] pub block_data: Vec, pub block_entities: Vec, pub entities: Option>, } -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] +#[derive(Clone, Debug)] pub struct SpongeV3Schematic { pub data_version: i32, pub metadata: HashMap, @@ -172,57 +132,263 @@ pub struct SpongeV3Schematic { pub entities: Option>, } -#[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] +#[derive(Clone, Debug)] pub struct BlockContainer { pub palette: HashMap, - #[serde(deserialize_with = "read_blockdata")] pub block_data: Vec, pub block_entities: Vec, } -fn read_blockdata<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, -{ - let s: Vec = Deserialize::deserialize(deserializer)?; - Ok(read_varint_array(&s)) -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "PascalCase")] +#[derive(Debug, Clone)] pub struct BlockEntity { pub id: String, pub pos: [i32; 3], } -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "PascalCase")] +#[derive(Debug, Clone)] pub struct BlockEntityV3 { pub id: String, pub pos: [i32; 3], pub data: HashMap, } -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "PascalCase")] +#[derive(Debug, Clone)] pub struct Entity { pub id: String, pub pos: [i32; 3], } impl SchematicVersioned { - pub fn load_data(data: R) -> Result where R: Read { - let raw: SchematicRaw = fastnbt::from_reader(GzDecoder::new(data))?; - SchematicVersioned::try_from(raw) + pub fn load_data(data: R) -> Result where R: Read { + let nbt: HashMap = nbt::de::from_gzip_reader(data).map_err(|e| e.to_string())?; + let version = match nbt.get("Version") { + Some(version) => match version { + Value::Short(n) => *n as i32, + Value::Byte(n) => *n as i32, + Value::Int(n) => *n, + _ => return Err("Invalid schematic: Wrong Version Type".to_string()), + }, + None => return Err("Invalid schematic: Version not Found".to_string()), + }; + + match version { + 1 => Ok(SchematicVersioned::V1(SpongeV1Schematic::from_nbt(nbt)?)), + 2 => Ok(SchematicVersioned::V2(SpongeV2Schematic::from_nbt(nbt)?)), + 3 => Ok(SchematicVersioned::V3(SpongeV3Schematic::from_nbt(nbt)?)), + _ => Err("Invalid schematic: Unknown Version".to_string()), + } } - pub fn load(path: &PathBuf) -> Result { - let file = std::fs::File::open(path)?; + pub fn load(path: &PathBuf) -> Result { + let file = std::fs::File::open(path).map_err(|e| e.to_string())?; Self::load_data(file) } } +impl SpongeV1Schematic { + pub fn from_nbt(nbt: HashMap) -> Result { + Ok(Self { + metadata: match nbt.get("Metadata").ok_or("Invalid schematic: Metadata not found".to_string())? { + Value::Compound(metadata) => metadata.clone(), + _ => return Err("Invalid schematic: Metadata Wrong Type".to_string()), + }, + width: match nbt.get("Width").ok_or("Invalid schematic: Width not found".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Width Wrong Type".to_string()), + }, + height: match nbt.get("Height").ok_or("Invalid schematic: Height not found".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Height Wrong Type".to_string()), + }, + length: match nbt.get("Length").ok_or("Invalid schematic: Length not found".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Length Wrong Type".to_string()), + }, + offset: read_offset(nbt.get("Offset"))?, + palette_max: match nbt.get("PaletteMax").ok_or("Invalid schematic: PaletteMax not found".to_string())? { + Value::Int(p) => *p, + _ => return Err("Invalid schematic: PaletteMax Wrong Type".to_string()), + }, + palette: read_palette(nbt.get("Palette"))?, + block_data: read_blocks(nbt.get("BlockData"))?, + tile_entities: read_tile_entities(nbt.get("TileEntities"))?, + }) + } +} + +impl SpongeV2Schematic { + pub fn from_nbt(nbt: HashMap) -> Result { + Ok(Self{ + data_version: match nbt.get("DataVersion").ok_or("Invalid schematic: DataVersion Missing".to_string())? { + Value::Short(n) => *n as i32, + Value::Byte(n) => *n as i32, + Value::Int(n) => *n, + _ => return Err("Invalid schematic: DataVersion Wrong Type".to_string()), + }, + metadata: match nbt.get("Metadata").ok_or("Invalid schematic".to_string())? { + Value::Compound(m) => m.clone(), + _ => return Err("Invalid schematic: Metadata Wrong Type".to_string()), + }, + width: match nbt.get("Width").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Width Wrong Type".to_string()), + }, + height: match nbt.get("Height").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Height Wrong Type".to_string()), + }, + length: match nbt.get("Length").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic: Length Wrong Type".to_string()), + }, + offset: read_offset(nbt.get("Offset"))?, + palette_max: match nbt.get("PaletteMax").ok_or("Invalid schematic: PaletteMax Missing".to_string())? { + Value::Short(n) => *n as i32, + Value::Byte(n) => *n as i32, + Value::Int(n) => *n, + _ => return Err("Invalid schematic: PaletteMax Invalid Type".to_string()), + }, + palette: read_palette(nbt.get("Palette"))?, + block_data: read_blocks(nbt.get("BlockData"))?, + block_entities: read_tile_entities(nbt.get("BlockEntities"))?, + entities: None, + }) + } +} + +impl SpongeV3Schematic { + pub fn from_nbt(nbt: HashMap) -> Result { + Ok(Self{ + data_version: match nbt.get("DataVersion").ok_or("Invalid schematic".to_string())? { + Value::Int(d) => *d, + _ => return Err("Invalid schematic".to_string()), + }, + metadata: match nbt.get("Metadata").ok_or("Invalid schematic".to_string())? { + Value::Compound(m) => m.clone(), + _ => return Err("Invalid schematic".to_string()), + }, + width: match nbt.get("Width").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic".to_string()), + }, + height: match nbt.get("Height").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic".to_string()), + }, + length: match nbt.get("Length").ok_or("Invalid schematic".to_string())? { + Value::Short(n) => *n as u16, + Value::Byte(n) => *n as u16, + _ => return Err("Invalid schematic".to_string()), + }, + offset: read_offset(nbt.get("Offset"))?, + blocks: match nbt.get("Blocks").ok_or("Invalid schematic".to_string())? { + Value::Compound(b) => { + BlockContainer { + palette: read_palette(b.get("Palette"))?, + block_data: read_blocks(b.get("BlockData"))?, + block_entities: read_tile_entities(b.get("BlockEntities"))?, + } + } + _ => return Err("Invalid schematic".to_string()), + }, + entities: None, + }) + } +} + +fn read_tile_entities(tag: Option<&Value>) -> Result, String> { + match tag.ok_or("Invalid schematic: read_tile_entities not found".to_string())? { + Value::List(t) => { + let mut tile_entities = Vec::new(); + for te in t.iter() { + match te { + Value::Compound(te) => { + let id = match te.get("Id") { + None => return Err("Invalid schematic: Id Not Found".to_string()), + Some(id) => match id { + Value::String(id) => id.clone(), + _ => return Err("Invalid schematic: Id Wrong Type".to_string()), + }, + }; + let pos = read_offset(te.get("Pos"))?; + tile_entities.push(BlockEntity { id, pos }); + }, + _ => return Err("Invalid schematic: te Wrong Type".to_string()), + }; + } + Ok(tile_entities) + }, + Value::ByteArray(_) => Ok(vec![]), + _ => return Err("Invalid schematic: te wrong type".to_string()), + } +} + +#[inline] +fn read_offset(offset: Option<&Value>) -> Result<[i32; 3], String> { + match offset.ok_or("Invalid schematic: read_offset missing".to_string())? { + Value::IntArray(o) => match o.len() { + 3 => Ok([o[0], o[1], o[2]]), + _ => Err("Invalid schematic: Invalid IntArray".to_string()), + }, + Value::ByteArray(o) => match o.len() { + 3 => Ok([o[0] as i32, o[1] as i32, o[2] as i32]), + _ => Err("Invalid schematic: Invalid byteArray".to_string()), + }, + Value::List(l) => match l.len() { + 3 => { + let mut offset = [0; 3]; + for (i, v) in l.iter().enumerate() { + match v { + Value::Int(n) => offset[i] = *n, + Value::Byte(n) => offset[i] = *n as i32, + Value::Short(n) => offset[i] = *n as i32, + _ => return Err("Invalid schematic: read_offset invalid Number".to_string()), + }; + } + Ok(offset) + }, + _ => Err("Invalid schematic: Invalid List".to_string()), + } + _ => Err("Invalid schematic: read_offset".to_string()), + } +} + +#[inline] +fn read_palette(palette: Option<&Value>) -> Result, String> { + match palette.ok_or("Invalid schematic: read_palette missing".to_string())? { + Value::Compound(p) => { + let mut palette = HashMap::new(); + for (k, v) in p.iter() { + match v { + Value::Int(v) => { palette.insert(k.clone(), *v); }, + Value::Byte(v) => { palette.insert(k.clone(), *v as i32); }, + Value::Short(v) => { palette.insert(k.clone(), *v as i32); }, + _ => return Err("Invalid schematic: read_palette invalid Number".to_string()), + }; + } + Ok(palette) + }, + _ => Err("Invalid schematic: read_palette invalid Type".to_string()), + } +} + +#[inline] +fn read_blocks(blockdata: Option<&Value>) -> Result, String> { + match blockdata.ok_or("Invalid schematic: BlockData not found".to_string())? { + Value::ByteArray(b) => Ok(read_varint_array(b)), + _ => Err("Invalid schematic: Invalid BlockData".to_string()), + } +} + +#[inline] pub fn read_varint_array(read: &Vec) -> Vec { let mut data = Vec::new(); let mut value: i32 = 0;