diff --git a/crates/world/src/level_dat/abilities.rs b/crates/world/src/level_dat/abilities.rs index 4da0c7b..62804d2 100644 --- a/crates/world/src/level_dat/abilities.rs +++ b/crates/world/src/level_dat/abilities.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use bedrockrs_nbt as nbt; - use bedrockrs_shared::world::permissions_level::PermissionLevel; use crate::error::WorldError; @@ -55,13 +53,13 @@ pub struct LevelDatAbilities { impl LevelDatAbilities { /// Parses a given [`nbt::Value`] into a [`LevelDatAbilities`] object. - pub fn parse(tag: nbt::Value) -> Result { + pub fn parse(tag: nbtx::Value) -> Result { fn get_byte_as_bool( - map: &mut HashMap, + map: &mut HashMap, key: &str, ) -> Result { match map.remove(key) { - Some(nbt::Value::Byte(v)) => Ok(v != 0), + Some(nbtx::Value::Byte(v)) => Ok(v != 0), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Byte, got {:?}", key, other @@ -74,11 +72,11 @@ impl LevelDatAbilities { } fn get_byte_as_bool_option( - map: &mut HashMap, + map: &mut HashMap, key: &str, ) -> Result, WorldError> { match map.remove(key) { - Some(nbt::Value::Byte(v)) => Ok(Some(v != 0)), + Some(nbtx::Value::Byte(v)) => Ok(Some(v != 0)), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Byte, got {:?}", key, other @@ -87,9 +85,9 @@ impl LevelDatAbilities { } } - fn get_int32(map: &mut HashMap, key: &str) -> Result { + fn get_int32(map: &mut HashMap, key: &str) -> Result { match map.remove(key) { - Some(nbt::Value::Int(v)) => Ok(v), + Some(nbtx::Value::Int(v)) => Ok(v), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Int32, got {:?}", key, other @@ -102,11 +100,11 @@ impl LevelDatAbilities { } fn get_int32_option( - map: &mut HashMap, + map: &mut HashMap, key: &str, ) -> Result, WorldError> { match map.remove(key) { - Some(nbt::Value::Int(v)) => Ok(Some(v)), + Some(nbtx::Value::Int(v)) => Ok(Some(v)), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Int32, got {:?}", key, other @@ -115,9 +113,9 @@ impl LevelDatAbilities { } } - fn get_int64(map: &mut HashMap, key: &str) -> Result { + fn get_int64(map: &mut HashMap, key: &str) -> Result { match map.remove(key) { - Some(nbt::Value::Long(v)) => Ok(v), + Some(nbtx::Value::Long(v)) => Ok(v), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Int64, got {:?}", key, other @@ -129,9 +127,9 @@ impl LevelDatAbilities { } } - fn get_f32(map: &mut HashMap, key: &str) -> Result { + fn get_f32(map: &mut HashMap, key: &str) -> Result { match map.remove(key) { - Some(nbt::Value::Float(v)) => Ok(v), + Some(nbtx::Value::Float(v)) => Ok(v), Some(other) => Err(WorldError::FormatError(format!( "Expected `{}` in LevelDat abilities to be of type Float32, got {:?}", key, other @@ -144,7 +142,7 @@ impl LevelDatAbilities { } match tag { - nbt::Value::Compound(mut map) => Ok(Self { + nbtx::Value::Compound(mut map) => Ok(Self { attack_mobs: get_byte_as_bool(&mut map, "attackmobs")?, attack_players: get_byte_as_bool(&mut map, "attackplayers")?, redstone_interact: get_byte_as_bool(&mut map, "doorsandswitches")?, diff --git a/crates/world/src/level_dat/level_dat.rs b/crates/world/src/level_dat/level_dat.rs index 6f1894e..71a380e 100644 --- a/crates/world/src/level_dat/level_dat.rs +++ b/crates/world/src/level_dat/level_dat.rs @@ -3,8 +3,6 @@ use std::fs::File; use std::io::{Cursor, Read}; use std::path::{Path, PathBuf}; -use bedrockrs_nbt::endian::little_endian::NbtLittleEndian; -use bedrockrs_nbt::NbtTag; use byteorder::{LittleEndian, ReadBytesExt}; use bedrockrs_shared::world::difficulty::Difficulty; @@ -68,220 +66,3 @@ pub struct LevelDat { /// If cheats are on. (NBT entry: `commandsEnabled`) pub cheats: bool, } - -impl LevelDat { - /// Opens the `level.dat` file from a given Minecraft Bedrock world directory. - /// - /// Returns the header of the `level.dat` file and a [`LevelDat`] object, the header consists - /// of two `i32` integers which represent the version of the Minecraft Bedrock world - /// (which currently is `10`) and the length of the `level.dat` file excluding the header size. - pub fn open(directory: &Path) -> Result<(i32, i32, Self), WorldError> { - // Open the level.dat file - // TODO find out why there is a level.dat_old file as well and how it can be utilised - let mut file = File::open(directory.join("level.dat")) - .map_err(|e| WorldError::FormatError(e.to_string()))?; - - // Read the entire level.dat file - let mut data = vec![]; - match file.read_to_end(&mut data) { - Ok(_) => {} - Err(e) => { - return Err(WorldError::FormatError(e.to_string())); - } - }; - - // Build a ByteStreamRead with the file contents - let mut stream = Cursor::new(&data); - - // Read the worlds format version - let version = match stream.read_i32::() { - Ok(v) => v, - Err(e) => { - return Err(WorldError::FormatError(e.to_string())); - } - }; - - // Read the level.dat size (without the header) - let length = match stream.read_i32::() { - Ok(v) => v, - Err(e) => { - return Err(WorldError::FormatError(e.to_string())); - } - }; - - // Read the uncompressed nbt tag - - let pos = stream.position(); // TODO: utility function for this - let mut new_cur = Cursor::new(stream.into_inner().as_slice()); - new_cur.set_position(pos); - - let (_, nbt) = match NbtTag::nbt_deserialize::(&mut new_cur) { - Ok(v) => v, - Err(e) => { - return Err(WorldError::NbtError(e)); - } - }; - - // Parse the nbt tag into the LevelDat struct - let level_dat = match Self::parse(nbt) { - Ok(v) => v, - Err(e) => { - return Err(e); - } - }; - - Ok((version, length, level_dat)) - } - - /// Parses a given [`NbtTag`] into a [`LevelDat`] object. - pub fn parse(tag: NbtTag) -> Result { - fn get_string(map: &mut HashMap, key: &str) -> Result { - match map.remove(key) { - Some(NbtTag::String(v)) => Ok(v), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type String, got {:?}", - key, other - ))), - None => Err(WorldError::FormatError(format!( - "Missing field `{}` in LevelDat", - key - ))), - } - } - - fn get_byte_as_bool( - map: &mut HashMap, - key: &str, - ) -> Result { - match map.remove(key) { - Some(NbtTag::Byte(v)) => Ok(v != 0), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type Byte, got {:?}", - key, other - ))), - None => Err(WorldError::FormatError(format!( - "Missing field `{}` in LevelDat", - key - ))), - } - } - - fn get_int32(map: &mut HashMap, key: &str) -> Result { - match map.remove(key) { - Some(NbtTag::Int32(v)) => Ok(v), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type Int32, got {:?}", - key, other - ))), - None => Err(WorldError::FormatError(format!( - "Missing field `{}` in LevelDat", - key - ))), - } - } - - fn get_int32_option( - map: &mut HashMap, - key: &str, - ) -> Result, WorldError> { - match map.remove(key) { - Some(NbtTag::Int32(v)) => Ok(Some(v)), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type Int32, got {:?}", - key, other - ))), - None => Ok(None), - } - } - - fn get_int64(map: &mut HashMap, key: &str) -> Result { - match map.remove(key) { - Some(NbtTag::Int64(v)) => Ok(v), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type Int64, got {:?}", - key, other - ))), - None => Err(WorldError::FormatError(format!( - "Missing field `{}` in LevelDat", - key - ))), - } - } - - fn get_compound( - map: &mut HashMap, - key: &str, - ) -> Result, WorldError> { - match map.remove(key) { - Some(NbtTag::Compound(v)) => Ok(v), - Some(other) => Err(WorldError::FormatError(format!( - "Expected `{}` in LevelDat to be of type Compound, got {:?}", - key, other - ))), - None => Err(WorldError::FormatError(format!( - "Missing field `{}` in LevelDat", - key - ))), - } - } - - match tag { - // It must be a compound tag - NbtTag::Compound(mut map) => Ok(Self { - level_name: get_string(&mut map, "LevelName")?, - format_version: get_int32(&mut map, "StorageVersion")?, - abilities: LevelDatAbilities::parse(match map.remove("abilities") { - Some(v) => v, - None => Err(WorldError::FormatError(format!( - "Missing field `abilities` in LevelDat" - )))?, - })?, - experiments: { - let nbt = get_compound(&mut map, "experiments")?; - let mut experiments = HashMap::new(); - - for (name, tag) in nbt.iter() { - experiments.insert(name.clone(), match tag { - NbtTag::Byte(v) => { *v != 0 } - other => { Err(WorldError::FormatError(format!("Expected `{}` in LevelDat experiments to be of type Byte, got {:?}", name, other)))? } - }); - } - - experiments - }, - difficulty: match get_int32(&mut map, "Difficulty")? { - 0 => Difficulty::Peaceful, - 1 => Difficulty::Easy, - 2 => Difficulty::Normal, - 3 => Difficulty::Hard, - other => Err(WorldError::FormatError(format!( - "Value for `Difficulty` is out of bounds, got {:?}", - other - )))?, - }, - dimension: match get_int32_option(&mut map, "Dimension")? { - Some(1) => Some(Dimension::Overworld), - Some(2) => Some(Dimension::Nether), - Some(3) => Some(Dimension::End), - Some(other) => Err(WorldError::FormatError(format!( - "Value for `Dimension` is out of bounds, got {:?}", - other - )))?, - None => None, - }, - bonus_chest_enabled: get_byte_as_bool(&mut map, "bonusChestEnabled")?, - bonus_chest_spawned: get_byte_as_bool(&mut map, "bonusChestSpawned")?, - seed: get_int64(&mut map, "RandomSeed")?, - current_tick: get_int64(&mut map, "currentTick")?, - spawn_x: get_int32(&mut map, "SpawnX")?, - spawn_y: get_int32(&mut map, "SpawnY")?, - spawn_z: get_int32(&mut map, "SpawnZ")?, - cheats: get_byte_as_bool(&mut map, "commandsEnabled")?, - }), - other => Err(WorldError::FormatError(format!( - "Expected root tag in LevelDat to be of type Compound, got {:?}", - other - ))), - } - } -} diff --git a/crates/world/src/world.rs b/crates/world/src/world.rs index 5f1e1c2..4b89005 100644 --- a/crates/world/src/world.rs +++ b/crates/world/src/world.rs @@ -21,12 +21,13 @@ impl World { // Read the world settings/specifications from the level.dat file. // (in world/level.dat and world/level.dat_old) - let (version, _, level_dat) = match LevelDat::open(&directory) { - Ok(v) => v, - Err(e) => { - return Err(e); - } - }; + todo!("Help"); + // let (version, _, level_dat) = match LevelDat::open(&directory) { + // Ok(v) => v, + // Err(e) => { + // return Err(e); + // } + // }; // Read the world name from the levelname.txt file // (in world/levelname.txt) @@ -43,7 +44,7 @@ impl World { } }, level_dat, - format_version: version, + format_version: 2, }) } }