Skip to content

Commit

Permalink
src: add little endian support
Browse files Browse the repository at this point in the history
  • Loading branch information
Surasia authored and Surasia committed Nov 4, 2024
1 parent e98b27a commit 2bbc25f
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 194 deletions.
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "hkscdis-rs"
description = "A HavokScript 5.1 disassembler written in Rust."
version = "0.2.0"
version = "0.2.1"
edition = "2021"
license = "GPL-3.0-only"
keywords = ["hksc", "hkscdis", "havok", "havokscript", "disassembler"]
Expand Down
10 changes: 5 additions & 5 deletions src/cli/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn print_header(header: &HSHeader) {
);
cprintln!(
"<yellow>- Extensions:<yellow> <bright-cyan>{}<bright-cyan>",
header.compatability
header.features
);
println!();
}
Expand Down Expand Up @@ -182,13 +182,13 @@ fn print_structures(structs: &Vec<HSStructBlock>) {
"<yellow>-<yellow> <bright-blue>{}<bright-blue>",
struc.header.name
);
if struc.extended_structs.is_empty() {
if struc.inherited_structs.is_empty() {
cprintln!("<bright-blue>:<bright-blue>");
}
for extend in &struc.extended_structs {
for inherites in &struc.inherited_structs {
cprintln!(
"<green> EXTENDS<green> <bright-blue>{}<bright-blue>:",
extend
"<green> INHERITED FROM<green> <bright-blue>{}<bright-blue>:",
inherites
);
}
for member in &struc.members {
Expand Down
53 changes: 28 additions & 25 deletions src/common/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
//! Extensions to `BufReader`.
//!
//! Implements additional reading methods for `BufReader`:
//! * `read_fixed_string`: Reads a fixed-length string from a buffer
//! * `read_enumerable`: Reads multiple instances of a type that implements `Readable` into a Vec
//! * `read_header_enumerable`: Reads multiple instances of a type that implements `HeaderReadable` into a Vec
//! * `read_fixed_string`: Reads a fixed-length string from a buffer.
//! * `read_enumerable`: Reads multiple instances of a type that implements `Readable` into a `Vec`.
//! * `read_header_enumerable`: Reads multiple instances of a type that implements `HeaderReadable` into a `Vec`.
//!
//! These extensions require `Read + Seek` bounds.
use crate::{common::errors::HkscError, loader::hs_header::HSHeader};

use byteorder::ByteOrder;
use std::io::{BufRead, BufReader, Read, Seek};

/// `Readable` trait that ensures a `read` method is declared.
pub trait Readable {
/// Reads data from a reader implementing `BufRead`, `BufReaderExt`, and `Seek`.
fn read<R>(&mut self, reader: &mut R) -> Result<(), HkscError>
where
R: BufRead + BufReaderExt + Seek;
fn read<T: ByteOrder>(&mut self, reader: &mut impl BufReaderExt) -> Result<(), HkscError>;
}

/// `HeaderReadable` trait that ensures a `read` method is declared with a `HSHeader` argument.
pub trait HeaderReadable {
/// Reads data from a reader implementing `BufRead`, `BufReaderExt`, and `Seek`, using header information.
fn read<R>(&mut self, reader: &mut R, header: &HSHeader) -> Result<(), HkscError>
where
R: BufRead + BufReaderExt + Seek;
fn read<T: ByteOrder>(
&mut self,
reader: &mut impl BufReaderExt,
header: &HSHeader,
) -> Result<(), HkscError>;
}

/// Extension trait for `BufReader` to add custom reading methods.
Expand All @@ -35,54 +35,57 @@ where
/// Reads a fixed-length UTF-8 encoded string.
///
/// # Arguments
/// * `length` - Number of bytes to read
/// * `length` - Number of bytes to read.
///
/// # Returns
/// The read string on success, or an error on failure
fn read_fixed_string(&mut self, length: usize) -> Result<String, HkscError> {
/// The read string on success, or an error on failure.
fn read_fixed_string<T: ByteOrder>(&mut self, length: usize) -> Result<String, HkscError> {
let mut buffer = vec![0; length];
self.read_exact(&mut buffer)?;

if buffer == [255, 255, 255, 255] {
return Ok(String::new()); // Return empty string if all bytes are 0xFF
return Ok(String::new()); // Return empty string if all bytes are 0xFF.
}

let string = String::from_utf8(buffer)?;

Ok(string)
}

/// Reads multiple instances of a type into a Vec.
/// Reads multiple instances of a type into a `Vec`.
///
/// # Arguments
/// * `count` - Number of instances to read
/// * `count` - Number of instances to read.
///
/// # Returns
/// Vec of read instances on success, or an error on failure
fn read_enumerable<T: Default + Readable>(&mut self, count: u64) -> Result<Vec<T>, HkscError>
/// `Vec` of read instances on success, or an error on failure.
fn read_enumerable<T: Default + Readable, R: ByteOrder>(
&mut self,
count: u64,
) -> Result<Vec<T>, HkscError>
where
Self: Sized,
Vec<T>: FromIterator<T>,
{
let enumerables = (0..count)
.map(|_| -> Result<T, HkscError> {
let mut enumerable = T::default();
enumerable.read(self)?;
enumerable.read::<R>(self)?;
Ok(enumerable)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(enumerables)
}

/// Reads multiple instances of a type into a Vec, using header information.
/// Reads multiple instances of a type into a `Vec`, using header information.
///
/// # Arguments
/// * `count` - Number of instances to read
/// * `header` - The `HSHeader` containing format information
/// * `count` - Number of instances to read.
/// * `header` - The `HSHeader` containing format information.
///
/// # Returns
/// Vec of read instances on success, or an error on failure
fn read_header_enumerable<T: Default + HeaderReadable>(
/// `Vec` of read instances on success, or an error on failure.
fn read_header_enumerable<T: Default + HeaderReadable, R: ByteOrder>(
&mut self,
count: u64,
header: &HSHeader,
Expand All @@ -94,7 +97,7 @@ where
let enumerables = (0..count)
.map(|_| -> Result<T, HkscError> {
let mut enumerable = T::default();
enumerable.read(self, header)?;
enumerable.read::<R>(self, header)?;
Ok(enumerable)
})
.collect::<Result<Vec<_>, _>>()?;
Expand Down
36 changes: 25 additions & 11 deletions src/loader/hs.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use super::{
hs_enums::HSEnum, hs_function::HSFunction, hs_header::HSHeader, hs_structure::HSStructBlock,
hs_enums::HSEnum,
hs_function::HSFunction,
hs_header::{HSFeatures, HSHeader},
hs_structure::HSStructBlock,
};
use crate::{
common::errors::HkscError,
common::extensions::{BufReaderExt, HeaderReadable},
};

use byteorder::{ReadBytesExt, LE};
use byteorder::{ByteOrder, ReadBytesExt, BE, LE};
use std::{fs::File, io::BufReader};

#[derive(Default)]
Expand All @@ -28,20 +31,31 @@ pub struct HavokScriptFile {
impl HavokScriptFile {
pub fn read(&mut self, reader: &mut BufReader<File>) -> Result<(), HkscError> {
self.header.read(reader)?;
self.enums = reader.read_enumerable::<HSEnum>(self.header.enum_count.into())?;
self.main_function.read(reader, &self.header)?;

reader.seek_relative(4)?; // Padding?
if self.header.is_little_endian {
self.enums = reader.read_enumerable::<HSEnum, LE>(self.header.enum_count.into())?;
self.main_function.read::<LE>(reader, &self.header)?;
reader.seek_relative(4)?;
self.read_structures::<LE>(reader)?;
} else {
self.enums = reader.read_enumerable::<HSEnum, BE>(self.header.enum_count.into())?;
self.main_function.read::<BE>(reader, &self.header)?;
reader.seek_relative(4)?;
self.read_structures::<BE>(reader)?;
}
Ok(())
}

// End of the file is indicated by a u64 with a value of 0, so we loop until we find it,
// reading structs as we go
while reader.read_u64::<LE>()? != 0 {
pub fn read_structures<T: ByteOrder>(
&mut self,
reader: &mut BufReader<File>,
) -> Result<(), HkscError> {
while self.header.features.contains(HSFeatures::STRUCTURES) && reader.read_u64::<T>()? != 0
{
reader.seek_relative(-8)?; // Compensate for the previous read
let mut structure = HSStructBlock::default();
structure.read(reader, &self.header)?;
structure.read::<T>(reader, &self.header)?;
self.structs.push(structure);
}

Ok(())
}
}
26 changes: 12 additions & 14 deletions src/loader/hs_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ use crate::{
common::extensions::{BufReaderExt, HeaderReadable},
};

use byteorder::{ReadBytesExt, BE};
use std::{
fmt::{Display, Formatter},
io::BufRead,
};
use byteorder::{ByteOrder, ReadBytesExt};
use std::fmt::{Display, Formatter};

/// Possible values for a (valid) `HavokScript` constant.
pub enum HSValue {
Expand Down Expand Up @@ -53,24 +50,25 @@ impl Display for HSConstant {
}

impl HeaderReadable for HSConstant {
fn read<R>(&mut self, reader: &mut R, header: &HSHeader) -> Result<(), HkscError>
where
R: BufRead + BufReaderExt,
{
fn read<T: ByteOrder>(
&mut self,
reader: &mut impl BufReaderExt,
header: &HSHeader,
) -> Result<(), HkscError> {
let type_byte = reader.read_u8()?;
self.type_ = HSType::try_from(type_byte).map_err(|_| HkscError::UnknownType(type_byte))?;

self.value = match self.type_ {
HSType::TNIL => Some(HSValue::Nil),
HSType::TLIGHTUSERDATA => match header.t_size {
4 => Some(HSValue::LightUserData(reader.read_u32::<BE>()?.into())),
8 => Some(HSValue::LightUserData(reader.read_u64::<BE>()?)),
4 => Some(HSValue::LightUserData(reader.read_u32::<T>()?.into())),
8 => Some(HSValue::LightUserData(reader.read_u64::<T>()?)),
_ => return Err(HkscError::InvalidLightUserDataSize(header.t_size)),
},
HSType::TBOOLEAN => Some(HSValue::Boolean(reader.read_u8()? != 0)),
HSType::TSTRING => Some(HSValue::String(read_string(reader, header)?)),
HSType::TNUMBER => Some(read_number(reader, header)?),
HSType::TUI64 => Some(HSValue::Ui64(reader.read_u64::<BE>()?)),
HSType::TSTRING => Some(HSValue::String(read_string::<T>(reader, header)?)),
HSType::TNUMBER => Some(read_number::<T>(reader, header)?),
HSType::TUI64 => Some(HSValue::Ui64(reader.read_u64::<T>()?)),
_ => return Err(HkscError::UnsupportedConstantType(type_byte)),
};

Expand Down
47 changes: 24 additions & 23 deletions src/loader/hs_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use crate::{
common::extensions::{BufReaderExt, HeaderReadable},
};

use byteorder::{ReadBytesExt, BE, LE};
use std::io::BufRead;
use byteorder::{ByteOrder, ReadBytesExt};

#[derive(Debug, Default)]
/// Local variable information for a function.
Expand All @@ -19,13 +18,14 @@ pub struct HSFunctionDebugInfoLocals {
}

impl HeaderReadable for HSFunctionDebugInfoLocals {
fn read<R>(&mut self, reader: &mut R, header: &HSHeader) -> Result<(), HkscError>
where
R: BufRead + BufReaderExt,
{
self.local_name = read_string(reader, header)?;
self.start = reader.read_u32::<LE>()?;
self.end = reader.read_u32::<LE>()?;
fn read<T: ByteOrder>(
&mut self,
reader: &mut impl BufReaderExt,
header: &HSHeader,
) -> Result<(), HkscError> {
self.local_name = read_string::<T>(reader, header)?;
self.start = reader.read_u32::<T>()?;
self.end = reader.read_u32::<T>()?;
Ok(())
}
}
Expand Down Expand Up @@ -56,29 +56,30 @@ pub struct HSFunctionDebugInfo {
}

impl HeaderReadable for HSFunctionDebugInfo {
fn read<R>(&mut self, reader: &mut R, header: &HSHeader) -> Result<(), HkscError>
where
R: BufRead + BufReaderExt,
{
self.line_count = reader.read_u32::<BE>()?;
self.locals_count = reader.read_u32::<BE>()?;
self.up_value_count = reader.read_u32::<BE>()?;
self.line_begin = reader.read_u32::<BE>()?;
self.line_end = reader.read_u32::<BE>()?;
self.path = read_string(reader, header)?;
self.function_name = read_string(reader, header)?;
fn read<T: ByteOrder>(
&mut self,
reader: &mut impl BufReaderExt,
header: &HSHeader,
) -> Result<(), HkscError> {
self.line_count = reader.read_u32::<T>()?;
self.locals_count = reader.read_u32::<T>()?;
self.up_value_count = reader.read_u32::<T>()?;
self.line_begin = reader.read_u32::<T>()?;
self.line_end = reader.read_u32::<T>()?;
self.path = read_string::<T>(reader, header)?;
self.function_name = read_string::<T>(reader, header)?;

self.lines = (0..self.line_count)
.map(|_| reader.read_u32::<BE>())
.map(|_| reader.read_u32::<T>())
.collect::<Result<_, _>>()?;

self.locals = reader.read_header_enumerable::<HSFunctionDebugInfoLocals>(
self.locals = reader.read_header_enumerable::<HSFunctionDebugInfoLocals, T>(
self.locals_count.into(),
header,
)?;

self.up_values = (0..self.up_value_count)
.map(|_| read_string(reader, header))
.map(|_| read_string::<T>(reader, header))
.collect::<Result<_, _>>()?;

Ok(())
Expand Down
Loading

0 comments on commit 2bbc25f

Please sign in to comment.