use crate::tag::*; use azalea_buf::McBufWritable; use byteorder::{WriteBytesExt, BE}; use flate2::write::{GzEncoder, ZlibEncoder}; // use packed_simd_2::{i32x16, i32x2, i32x4, i32x8, i64x2, i64x4, i64x8}; use std::io::Write; #[inline] fn write_string(writer: &mut impl Write, string: &NbtString) { writer.write_u16::(string.len() as u16).unwrap(); writer.write_all(string.as_bytes()).unwrap(); } #[inline] fn write_compound(writer: &mut impl Write, value: &NbtCompound, end_tag: bool) { for (key, tag) in value.iter() { writer.write_u8(tag.id()).unwrap(); write_string(writer, key); match tag { Nbt::End => {} Nbt::Byte(value) => { writer.write_i8(*value).unwrap(); } Nbt::Short(value) => { writer.write_i16::(*value).unwrap(); } Nbt::Int(value) => { writer.write_i32::(*value).unwrap(); } Nbt::Long(value) => { writer.write_i64::(*value).unwrap(); } Nbt::Float(value) => { writer.write_f32::(*value).unwrap(); } Nbt::Double(value) => { writer.write_f64::(*value).unwrap(); } Nbt::ByteArray(value) => { write_byte_array(writer, value); } Nbt::String(value) => { write_string(writer, value); } Nbt::List(value) => { write_list(writer, value); } Nbt::Compound(value) => { write_compound(writer, value, true); } Nbt::IntArray(value) => { write_int_array(writer, value); } Nbt::LongArray(value) => { write_long_array(writer, value); } } } if end_tag { writer.write_u8(END_ID).unwrap(); } } #[inline] fn write_list(writer: &mut impl Write, value: &NbtList) { writer.write_u8(value.id()).unwrap(); match value { NbtList::Empty => writer.write_all(&[0; 4]).unwrap(), NbtList::Byte(l) => { writer.write_i32::(l.len() as i32).unwrap(); let l = l.as_slice(); writer // convert [i8] into [u8] .write_all(unsafe { std::slice::from_raw_parts(l.as_ptr() as *const u8, l.len()) }) .unwrap(); } NbtList::Short(l) => { writer.write_i32::(l.len() as i32).unwrap(); for &v in l { writer.write_i16::(v).unwrap(); } } NbtList::Int(l) => write_int_array(writer, l), NbtList::Long(l) => write_long_array(writer, l), NbtList::Float(l) => { writer.write_i32::(l.len() as i32).unwrap(); for &v in l { writer.write_f32::(v).unwrap(); } } NbtList::Double(l) => { writer.write_i32::(l.len() as i32).unwrap(); for &v in l { writer.write_f64::(v).unwrap(); } } NbtList::ByteArray(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_byte_array(writer, v); } } NbtList::String(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_string(writer, v); } } NbtList::List(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_list(writer, v); } } NbtList::Compound(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_compound(writer, v, true); } } NbtList::IntArray(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_int_array(writer, v); } } NbtList::LongArray(l) => { writer.write_i32::(l.len() as i32).unwrap(); for v in l { write_long_array(writer, v); } } } } #[inline] fn write_byte_array(writer: &mut impl Write, value: &[u8]) { writer.write_u32::(value.len() as u32).unwrap(); writer.write_all(value).unwrap(); } #[inline] fn write_int_array(writer: &mut impl Write, array: &[i32]) { writer.write_i32::(array.len() as i32).unwrap(); for &item in array { writer.write_i32::(item).unwrap(); } // (disabled for now since i realized packed_simd to_be does not work as // expected) // flip the bits to big endian with simd // let mut position = 0; // // x16 // while array.len() - position >= 16 { // let l = unsafe { // i32x16::from_slice_unaligned_unchecked(&array[position..]) }; let // l = l.to_be(); let l = unsafe { std::mem::transmute::(l) }; writer.write_all(&l).unwrap(); // position += 16; // } // // x8 // if array.len() - position >= 8 { // let l = unsafe { // i32x8::from_slice_unaligned_unchecked(&array[position..]) }; // let l = l.to_be(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 8; // } // // x4 // if array.len() - position >= 4 { // let l = unsafe { // i32x4::from_slice_unaligned_unchecked(&array[position..]) }; // let l = l.to_be(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 4; // } // // x2 // if array.len() - position >= 2 { // let l = unsafe { // i32x2::from_slice_unaligned_unchecked(&array[position..]) }; // let l = l.to_be(); // let l = l.swap_bytes(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 2; // } // // x1 ... just a normal write_i32 // if array.len() - position >= 1 { // writer.write_i32::(array[position]).unwrap(); // } } #[inline] fn write_long_array(writer: &mut impl Write, l: &[i64]) { writer.write_i32::(l.len() as i32).unwrap(); for &item in l { writer.write_i64::(item).unwrap(); } // (disabled for now since i realized packed_simd to_be does not work as // expected) // // flip the bits to big endian with simd // let mut position = 0; // // x16 // while l.len() - position >= 8 { // let l = unsafe { // i64x8::from_slice_unaligned_unchecked(&l[position..]) }; // l.to_be(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 8; // } // // x4 // if l.len() - position >= 4 { // let l = unsafe { // i64x4::from_slice_unaligned_unchecked(&l[position..]) }; // l.to_be(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 4; // } // // x2 // if l.len() - position >= 2 { // let l = unsafe { // i64x2::from_slice_unaligned_unchecked(&l[position..]) }; // l.to_be(); // let l = unsafe { std::mem::transmute::(l) }; // writer.write_all(&l).unwrap(); // position += 2; // } // // x1 ... just a normal write_i32 // if l.len() - position >= 1 { // writer.write_i64::(l[position]).unwrap(); // } } impl Nbt { /// Write the compound tag as NBT data. /// /// # Panics /// /// Will panic if the tag is not a Compound or End tag. pub fn write(&self, writer: &mut impl Write) { match self { Nbt::Compound(value) => { write_compound(writer, value, false); } Nbt::End => { END_ID.write_into(writer).unwrap(); } _ => panic!("Not a compound tag"), } } /// Write the compound tag as NBT data compressed wtih zlib. /// /// # Errors /// /// Returns an `Err` if it's not a Compound or End tag. pub fn write_zlib(&self, writer: &mut impl Write) { let mut encoder = ZlibEncoder::new(writer, flate2::Compression::default()); self.write(&mut encoder) } /// Write the compound tag as NBT data compressed wtih gzip. /// /// # Errors /// /// Returns an `Err` if it's not a Compound or End tag. pub fn write_gzip(&self, writer: &mut impl Write) { let mut encoder = GzEncoder::new(writer, flate2::Compression::default()); self.write(&mut encoder) } } impl McBufWritable for Nbt { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { self.write(buf); Ok(()) } }