1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-23 21:17:45 +00:00

Simplify PBD parsing

This commit is contained in:
Joshua Goins 2024-04-30 16:17:04 -04:00
parent 0b77a3cfb0
commit 149e9bb25b
2 changed files with 83 additions and 59 deletions

View file

@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com> // SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use std::io::SeekFrom;
use binrw::{BinReaderExt, BinResult};
pub(crate) fn read_bool_from<T: std::convert::From<u8> + std::cmp::PartialEq>(x: T) -> bool { pub(crate) fn read_bool_from<T: std::convert::From<u8> + std::cmp::PartialEq>(x: T) -> bool {
x == T::from(1u8) x == T::from(1u8)
} }
@ -13,6 +16,29 @@ pub(crate) fn write_bool_as<T: std::convert::From<u8>>(x: &bool) -> T {
} }
} }
#[binrw::parser(reader, endian)]
pub(crate) fn strings_parser(base_offset: u64, strings_offset: &Vec<u16>) -> BinResult<Vec<String>> {
let mut strings: Vec<String> =
vec![];
for offset in strings_offset {
let string_offset = base_offset + *offset as u64;
let mut string = String::new();
reader.seek(SeekFrom::Start(string_offset as u64))?;
let mut next_char = reader.read_le::<u8>().unwrap() as char;
while next_char != '\0' {
string.push(next_char);
next_char = reader.read_le::<u8>().unwrap() as char;
}
strings.push(string);
}
Ok(strings)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -4,42 +4,73 @@
use std::io::{Cursor, Seek, SeekFrom}; use std::io::{Cursor, Seek, SeekFrom};
use crate::ByteSpan; use crate::ByteSpan;
use binrw::binrw; use binrw::{binread, binrw};
use binrw::{BinRead, BinReaderExt}; use binrw::{BinRead, BinReaderExt};
use crate::common_file_operations::strings_parser;
#[binrw] #[binread]
#[derive(Debug)]
#[br(import { data_offset: i32 })]
#[brw(little)]
struct RacialDeformer {
bone_count: i32,
#[br(count = bone_count)]
bone_name_offsets: Vec<u16>,
#[br(args(data_offset as u64, &bone_name_offsets), parse_with = strings_parser)]
#[br(restore_position)]
bone_names: Vec<String>,
#[br(if((bone_count & 1) != 0))]
#[br(temp)]
_padding: u16,
/// 4x3 matrix
#[br(count = bone_count)]
#[br(err_context("offset = {} bone count = {}", data_offset, bone_count))]
transform: Vec<[f32; 12]>,
}
#[binread]
#[derive(Debug)] #[derive(Debug)]
#[brw(little)] #[brw(little)]
struct PreBoneDeformerItem { struct PreBoneDeformerItem {
body_id: u16, body_id: u16, // the combined body id like 0101
link_index: u16, link_index: i16,
#[br(pad_after = 4)] #[br(pad_after = 4)]
data_offset: u32, #[br(temp)]
data_offset: i32,
#[br(args { data_offset: data_offset })]
#[br(seek_before = SeekFrom::Start(data_offset as u64))]
#[br(restore_position)]
deformer: RacialDeformer
} }
#[binrw] #[binread]
#[derive(Debug)] #[derive(Debug)]
#[brw(little)] #[brw(little)]
struct PreBoneDeformerLink { struct PreBoneDeformerLink {
#[br(pad_after = 4)] parent_index: i16,
next_index: i16, first_child_index: i16,
next_item_index: u16, next_sibling_index: i16,
deformer_index: u16,
} }
#[binrw] #[binread]
#[derive(Debug)] #[derive(Debug)]
#[brw(little)] #[brw(little)]
struct PreBoneDeformerHeader { struct PreBoneDeformerHeader {
count: u32, count: i32,
#[br(count = count)] #[br(count = count)]
items: Vec<PreBoneDeformerItem>, items: Vec<PreBoneDeformerItem>,
#[br(count = count)] #[br(count = count)]
links: Vec<PreBoneDeformerLink>, links: Vec<PreBoneDeformerLink>,
#[br(ignore)]
raw_data: Vec<u8>,
} }
pub struct PreBoneDeformer { pub struct PreBoneDeformer {
@ -64,9 +95,7 @@ impl PreBoneDeformer {
/// Reads an existing PBD file /// Reads an existing PBD file
pub fn from_existing(buffer: ByteSpan) -> Option<PreBoneDeformer> { pub fn from_existing(buffer: ByteSpan) -> Option<PreBoneDeformer> {
let mut cursor = Cursor::new(buffer); let mut cursor = Cursor::new(buffer);
let mut header = PreBoneDeformerHeader::read(&mut cursor).ok()?; let mut header = PreBoneDeformerHeader::read(&mut cursor).unwrap();
header.raw_data = buffer.to_vec();
Some(PreBoneDeformer { header }) Some(PreBoneDeformer { header })
} }
@ -88,57 +117,26 @@ impl PreBoneDeformer {
.find(|x| x.body_id == from_body_id)?; .find(|x| x.body_id == from_body_id)?;
let mut next = &self.header.links[item.link_index as usize]; let mut next = &self.header.links[item.link_index as usize];
if next.next_index == -1 { if next.next_sibling_index == -1 {
return None; return None;
} }
let mut bones = vec![]; let mut bones = vec![];
let mut cursor = Cursor::new(&self.header.raw_data);
loop { loop {
cursor.seek(SeekFrom::Start(item.data_offset as u64)).ok()?; for i in 0..item.deformer.bone_count {
let bone_name_count = cursor.read_le::<u32>().unwrap() as usize;
let string_offsets_base = item.data_offset as usize + core::mem::size_of::<u32>();
cursor
.seek(SeekFrom::Start(string_offsets_base as u64))
.ok()?;
let mut strings_offset = vec![];
for _ in 0..bone_name_count {
strings_offset.push(cursor.read_le::<u16>().unwrap());
}
let matrices_base = string_offsets_base + (bone_name_count + bone_name_count % 2) * 2;
cursor.seek(SeekFrom::Start(matrices_base as u64)).ok()?;
let mut matrices = vec![];
for _ in 0..bone_name_count {
matrices.push(cursor.read_le::<[f32; 12]>().unwrap());
}
for i in 0..bone_name_count {
let string_offset = item.data_offset as usize + strings_offset[i] as usize;
let mut string = String::new();
cursor.seek(SeekFrom::Start(string_offset as u64)).ok()?;
let mut next_char = cursor.read_le::<u8>().unwrap() as char;
while next_char != '\0' {
string.push(next_char);
next_char = cursor.read_le::<u8>().unwrap() as char;
}
let matrix = matrices[i];
bones.push(PreBoneDeformBone { bones.push(PreBoneDeformBone {
name: string, name: item.deformer.bone_names[i as usize].clone(),
deform: matrix, deform: item.deformer.transform[i as usize],
}); })
} }
next = &self.header.links[next.next_index as usize]; if next.parent_index == -1 {
item = &self.header.items[next.next_item_index as usize]; break;
}
next = &self.header.links[next.parent_index as usize];
item = &self.header.items[next.deformer_index as usize];
if item.body_id == to_body_id { if item.body_id == to_body_id {
break; break;