Something went wrong. Try again.
A DPS meter for AION 2
Something went wrong. Try again.
3.5 kB · 116 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117use crate::Error;
/// A cursor over little-endian data.#[derive(Debug, Clone)]pub(crate) struct Reader<'a> { bytes: &'a [u8], position: usize,}
impl<'a> Reader<'a> { pub fn new(bytes: &'a [u8]) -> Self { Self { bytes, position: 0 } }
pub fn at(bytes: &'a [u8], position: usize) -> Self { Self { bytes, position } }
pub fn position(&self) -> usize { self.position }
pub fn bytes(&mut self, len: usize) -> Result<&'a [u8], Error> { let bytes = self .bytes .get(self.position..) .and_then(|rest| rest.get(..len)) .ok_or(Error::Invalid("ended early"))?; self.position += len; Ok(bytes) }
pub fn skip(&mut self, len: usize) -> Result<(), Error> { self.bytes(len).map(|_| ()) }
pub fn array<const N: usize>(&mut self) -> Result<[u8; N], Error> { Ok(self.bytes(N)?.try_into().expect("read N bytes")) }
pub fn u8(&mut self) -> Result<u8, Error> { Ok(self.bytes(1)?[0]) }
pub fn u32(&mut self) -> Result<u32, Error> { self.array().map(u32::from_le_bytes) }
pub fn i32(&mut self) -> Result<i32, Error> { self.array().map(i32::from_le_bytes) }
pub fn u64(&mut self) -> Result<u64, Error> { self.array().map(u64::from_le_bytes) }
/// An Unreal `FString`: a length, then that many UTF-8 bytes, or UTF-16 /// units if it's negative, with a terminator either way. pub fn fstring(&mut self) -> Result<String, Error> { self.fstring_with(|_| {}) }
/// An `FString` whose UTF-16 units, but not its terminator, are first /// passed through `decode`. pub fn fstring_with(&mut self, decode: impl FnOnce(&mut [u8])) -> Result<String, Error> { let len = self.i32()?; if len == 0 { return Ok(String::new()); } if len > 0 { let bytes = self.bytes(len as usize)?; let (text, terminator) = bytes.split_at(bytes.len() - 1); if terminator != [0] { return Err(Error::Invalid("string isn't terminated")); } return String::from_utf8(text.to_vec()) .map_err(|_| Error::Invalid("string isn't UTF-8")); }
let bytes = self.bytes(len.unsigned_abs() as usize * 2)?; let (text, terminator) = bytes.split_at(bytes.len() - 2); if terminator != [0, 0] { return Err(Error::Invalid("string isn't terminated")); } let mut text = text.to_vec(); decode(&mut text); let units: Vec<u16> = text .as_chunks::<2>() .0 .iter() .map(|&unit| u16::from_le_bytes(unit)) .collect(); String::from_utf16(&units).map_err(|_| Error::Invalid("string isn't UTF-16")) }}
#[cfg(test)]mod tests { use pretty_assertions::assert_eq;
use super::*;
#[test] fn test_fstring() { let utf8 = [4, 0, 0, 0, b'a', b'b', b'c', 0]; assert_eq!(Reader::new(&utf8).fstring().unwrap(), "abc");
let utf16 = [0xfd, 0xff, 0xff, 0xff, b'h', 0, b'i', 0, 0, 0]; assert_eq!(Reader::new(&utf16).fstring().unwrap(), "hi");
assert_eq!(Reader::new(&[0, 0, 0, 0]).fstring().unwrap(), "");
let unterminated = [3, 0, 0, 0, b'a', b'b', b'c']; assert!(Reader::new(&unterminated).fstring().is_err()); }}