use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeTreeNode {
pub type_name: String,
pub name: String,
pub byte_size: i32,
pub index: i32,
pub type_flags: i32,
pub version: i32,
pub meta_flags: i32,
pub level: i32,
pub type_str_offset: u32,
pub name_str_offset: u32,
pub ref_type_hash: u64,
pub children: Vec<TypeTreeNode>,
}
impl TypeTreeNode {
pub fn new() -> Self {
Self {
type_name: String::new(),
name: String::new(),
byte_size: 0,
index: 0,
type_flags: 0,
version: 0,
meta_flags: 0,
level: 0,
type_str_offset: 0,
name_str_offset: 0,
ref_type_hash: 0,
children: Vec::new(),
}
}
pub fn with_info(type_name: String, name: String, byte_size: i32) -> Self {
Self {
type_name,
name,
byte_size,
..Default::default()
}
}
pub fn is_array(&self) -> bool {
self.type_name == "Array" || self.type_name.starts_with("vector")
}
pub fn is_aligned(&self) -> bool {
(self.meta_flags & 0x4000) != 0
}
pub fn size(&self) -> i32 {
self.byte_size
}
pub fn is_primitive(&self) -> bool {
matches!(
self.type_name.as_str(),
"bool"
| "char"
| "SInt8"
| "UInt8"
| "SInt16"
| "UInt16"
| "SInt32"
| "UInt32"
| "SInt64"
| "UInt64"
| "float"
| "double"
| "int"
| "string"
)
}
pub fn is_string(&self) -> bool {
self.type_name == "string"
}
pub fn is_numeric(&self) -> bool {
matches!(
self.type_name.as_str(),
"SInt8"
| "UInt8"
| "SInt16"
| "UInt16"
| "SInt32"
| "UInt32"
| "SInt64"
| "UInt64"
| "float"
| "double"
| "int"
)
}
pub fn is_boolean(&self) -> bool {
self.type_name == "bool"
}
pub fn find_child(&self, name: &str) -> Option<&TypeTreeNode> {
self.children.iter().find(|child| child.name == name)
}
pub fn find_child_mut(&mut self, name: &str) -> Option<&mut TypeTreeNode> {
self.children.iter_mut().find(|child| child.name == name)
}
pub fn child_names(&self) -> Vec<&str> {
self.children
.iter()
.map(|child| child.name.as_str())
.collect()
}
pub fn add_child(&mut self, child: TypeTreeNode) {
self.children.push(child);
}
pub fn remove_child(&mut self, name: &str) -> Option<TypeTreeNode> {
if let Some(pos) = self.children.iter().position(|child| child.name == name) {
Some(self.children.remove(pos))
} else {
None
}
}
pub fn depth(&self) -> i32 {
self.level
}
pub fn has_children(&self) -> bool {
!self.children.is_empty()
}
pub fn child_count(&self) -> usize {
self.children.len()
}
pub fn validate(&self) -> Result<(), String> {
if self.type_name.is_empty() {
return Err("Type name cannot be empty".to_string());
}
if self.byte_size < -1 {
return Err("Invalid byte size".to_string());
}
for (i, child) in self.children.iter().enumerate() {
child
.validate()
.map_err(|e| format!("Child {}: {}", i, e))?;
}
Ok(())
}
}
impl Default for TypeTreeNode {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeTree {
pub nodes: Vec<TypeTreeNode>,
pub string_buffer: Vec<u8>,
pub version: u32,
pub platform: u32,
pub has_type_dependencies: bool,
}
impl TypeTree {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
string_buffer: Vec::new(),
version: 0,
platform: 0,
has_type_dependencies: false,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
nodes: Vec::with_capacity(capacity),
string_buffer: Vec::new(),
version: 0,
platform: 0,
has_type_dependencies: false,
}
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn add_node(&mut self, node: TypeTreeNode) {
self.nodes.push(node);
}
pub fn find_node(&self, name: &str) -> Option<&TypeTreeNode> {
self.nodes.iter().find(|node| node.name == name)
}
pub fn find_node_mut(&mut self, name: &str) -> Option<&mut TypeTreeNode> {
self.nodes.iter_mut().find(|node| node.name == name)
}
pub fn node_names(&self) -> Vec<&str> {
self.nodes.iter().map(|node| node.name.as_str()).collect()
}
pub fn name_peek_prefix(&self) -> Option<(usize, String)> {
let root = self.nodes.first()?;
for (i, child) in root.children.iter().enumerate() {
if child.name == "m_Name" || child.name == "name" {
return Some((i + 1, child.name.clone()));
}
}
None
}
pub fn clear(&mut self) {
self.nodes.clear();
self.string_buffer.clear();
}
pub fn get_string(&self, offset: u32) -> Option<String> {
if offset as usize >= self.string_buffer.len() {
return None;
}
let start = offset as usize;
let end = self.string_buffer[start..]
.iter()
.position(|&b| b == 0)
.map(|pos| start + pos)
.unwrap_or(self.string_buffer.len());
String::from_utf8(self.string_buffer[start..end].to_vec()).ok()
}
pub fn add_string(&mut self, s: &str) -> u32 {
let offset = self.string_buffer.len() as u32;
self.string_buffer.extend_from_slice(s.as_bytes());
self.string_buffer.push(0); offset
}
pub fn validate(&self) -> Result<(), String> {
if self.nodes.is_empty() {
return Err("TypeTree has no nodes".to_string());
}
for (i, node) in self.nodes.iter().enumerate() {
node.validate()
.map_err(|e| format!("Root node {}: {}", i, e))?;
}
Ok(())
}
pub fn statistics(&self) -> TypeTreeStatistics {
let mut stats = (0usize, 0i32, 0usize, 0usize);
fn count_nodes(node: &TypeTreeNode, depth: i32, stats: &mut (usize, i32, usize, usize)) {
stats.0 += 1; stats.1 = stats.1.max(depth);
if node.is_primitive() {
stats.2 += 1; }
if node.is_array() {
stats.3 += 1; }
for child in &node.children {
count_nodes(child, depth + 1, stats);
}
}
for node in &self.nodes {
count_nodes(node, 0, &mut stats);
}
TypeTreeStatistics {
total_nodes: stats.0,
root_nodes: self.nodes.len(),
max_depth: stats.1,
primitive_count: stats.2,
array_count: stats.3,
string_buffer_size: self.string_buffer.len(),
}
}
}
impl Default for TypeTree {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeTreeStatistics {
pub total_nodes: usize,
pub root_nodes: usize,
pub max_depth: i32,
pub primitive_count: usize,
pub array_count: usize,
pub string_buffer_size: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TypeInfo {
pub class_id: i32,
pub class_name: String,
pub type_tree: TypeTree,
pub script_type_index: Option<i16>,
pub script_id: [u8; 16],
pub old_type_hash: [u8; 16],
}
impl TypeInfo {
pub fn new(class_id: i32, class_name: String) -> Self {
Self {
class_id,
class_name,
type_tree: TypeTree::new(),
script_type_index: None,
script_id: [0; 16],
old_type_hash: [0; 16],
}
}
pub fn is_script_type(&self) -> bool {
self.script_type_index.is_some()
}
}
#[derive(Debug, Clone, Default)]
pub struct TypeRegistry {
types: HashMap<i32, TypeInfo>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self {
types: HashMap::new(),
}
}
pub fn add_type(&mut self, type_info: TypeInfo) {
self.types.insert(type_info.class_id, type_info);
}
pub fn get_type(&self, class_id: i32) -> Option<&TypeInfo> {
self.types.get(&class_id)
}
pub fn class_ids(&self) -> Vec<i32> {
self.types.keys().copied().collect()
}
pub fn has_type(&self, class_id: i32) -> bool {
self.types.contains_key(&class_id)
}
pub fn clear(&mut self) {
self.types.clear();
}
pub fn len(&self) -> usize {
self.types.len()
}
pub fn is_empty(&self) -> bool {
self.types.is_empty()
}
}