use std::io::{Cursor, Read}; use byteorder::{ReadBytesExt, LE}; use num_traits::{AsPrimitive, Num}; use crate::framework::error::GameResult; use crate::framework::error::GameError::{ResourceLoadError, InvalidValue}; use crate::framework::filesystem; use crate::framework::context::Context; struct PxCharReader> { cursor: Cursor, bit_ptr: usize, } impl> PxCharReader { fn read_integer_shifted(&mut self, bits: usize) -> GameResult where O: 'static + Num + Copy, u128: AsPrimitive, u64: AsPrimitive, u32: AsPrimitive, u16: AsPrimitive, u8: AsPrimitive, { let shift = self.bit_ptr & 7; self.cursor.set_position((self.bit_ptr / 8) as u64); self.bit_ptr += bits; match bits { // fast paths for aligned bit sizes 0 => Ok((0u8).as_()), 8 if shift == 0 => Ok(self.cursor.read_u8()?.as_()), 16 if shift == 0 => Ok(self.cursor.read_u16::()?.as_()), 24 if shift == 0 => Ok(self.cursor.read_u24::()?.as_()), 32 if shift == 0 => Ok(self.cursor.read_u32::()?.as_()), 48 if shift == 0 => Ok(self.cursor.read_u48::()?.as_()), 64 if shift == 0 => Ok(self.cursor.read_u64::()?.as_()), 128 if shift == 0 => Ok(self.cursor.read_u128::()?.as_()), // paths for bit shifted numbers 1..=8 => Ok(((self.cursor.read_u16::()? >> shift) & ((1 << bits) - 1)).as_()), 9..=16 => Ok(((self.cursor.read_u24::()? >> shift) & ((1 << bits) - 1)).as_()), 17..=24 => Ok(((self.cursor.read_u32::()? >> shift) & ((1 << bits) - 1)).as_()), 25..=40 => Ok(((self.cursor.read_u48::()? >> shift) & ((1 << bits) - 1)).as_()), 41..=56 => Ok(((self.cursor.read_u64::()? >> shift) & ((1 << bits) - 1)).as_()), 57..=120 => Ok(((self.cursor.read_u128::()? >> shift) & ((1 << bits) - 1)).as_()), 121..=128 => { let mut result = self.cursor.read_u128::()? >> shift; result |= (self.cursor.read_u8()? as u128) << (128 - shift); Ok(result.as_()) } _ => Err(InvalidValue("Cannot read integers bigger than 128 bits.".to_owned())), } } fn read_ranged(&mut self, max_value: u32) -> GameResult where O: 'static + Num + Copy, u128: AsPrimitive, u64: AsPrimitive, u32: AsPrimitive, u16: AsPrimitive, u8: AsPrimitive, { self.read_integer_shifted((32 - max_value.next_power_of_two().leading_zeros()) as usize) } fn read_string(&mut self, max_length: u32) -> GameResult { let mut output = Vec::new(); let length = self.read_ranged::(max_length)?; output.reserve(length as usize); for _ in 0..length { output.push(self.read_integer_shifted::(8)?) } Ok(String::from_utf8_lossy(&output).to_string()) } } impl> Read for PxCharReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { // align to byte if self.bit_ptr & 7 != 0 { self.bit_ptr = (self.bit_ptr + 7) & !7; self.cursor.read_u8()?; } let result = self.cursor.read(buf); self.bit_ptr = (self.cursor.position() * 8) as usize; result } } pub struct PxChar {} impl PxChar { pub fn load_pxchar(path: &str, ctx: &mut Context) -> GameResult { let mut reader = PxCharReader { cursor: Cursor::new({ let mut stream = filesystem::open(ctx, path)?; let mut data = Vec::new(); stream.read_to_end(&mut data)?; data }), bit_ptr: 0, }; let mut magic_buf = [0u8; 6]; reader.read_exact(&mut magic_buf)?; if &magic_buf != b"PXCHAR" { return Err(ResourceLoadError("Invalid magic number.".to_string())); } let version = reader.read_u8()?; if version > 5 { return Err(ResourceLoadError("Unsupported version.".to_string())); } let string = reader.read_string(0x100)?; println!("{}", string); let description = reader.read_string(0x100)?; println!("{}", description); Ok(PxChar {}) } } #[test] fn test() { use crate::framework::filesystem::mount_vfs; use crate::framework::vfs::PhysicalFS; let mut ctx = crate::framework::context::Context::new(); mount_vfs(&mut ctx, Box::new(PhysicalFS::new("data".as_ref(), true))); println!("lol"); PxChar::load_pxchar("/Player.pxchar", &mut ctx).unwrap(); }