mirror of
https://github.com/redstrate/Physis.git
synced 2025-07-20 07:47:45 +00:00
Begin parsing LVB files
These are actually quite useful, and contains stuff like the LGBs used for the territory.
This commit is contained in:
parent
b04121dfba
commit
a8cb676e97
3 changed files with 224 additions and 0 deletions
|
@ -54,6 +54,18 @@ pub(crate) fn strings_parser(
|
||||||
Ok(strings)
|
Ok(strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[binrw::parser(reader)]
|
||||||
|
pub(crate) fn read_string_until_null() -> BinResult<String> {
|
||||||
|
let mut string = String::new();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
|
||||||
fn read_half1(data: [u16; 1]) -> Half1 {
|
fn read_half1(data: [u16; 1]) -> Half1 {
|
||||||
Half1 {
|
Half1 {
|
||||||
value: f16::from_bits(data[0]),
|
value: f16::from_bits(data[0]),
|
||||||
|
|
|
@ -143,6 +143,9 @@ pub mod uwb;
|
||||||
/// Reading LCB files
|
/// Reading LCB files
|
||||||
pub mod lcb;
|
pub mod lcb;
|
||||||
|
|
||||||
|
/// Reading LVB files
|
||||||
|
pub mod lvb;
|
||||||
|
|
||||||
mod bcn;
|
mod bcn;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|
209
src/lvb.rs
Normal file
209
src/lvb.rs
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::io::SeekFrom;
|
||||||
|
|
||||||
|
use crate::ByteSpan;
|
||||||
|
use crate::common_file_operations::read_bool_from;
|
||||||
|
use crate::common_file_operations::read_string_until_null;
|
||||||
|
use binrw::BinRead;
|
||||||
|
use binrw::BinReaderExt;
|
||||||
|
use binrw::BinResult;
|
||||||
|
use binrw::binread;
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[brw(little)]
|
||||||
|
#[brw(magic = b"LVB1")]
|
||||||
|
pub struct Lvb {
|
||||||
|
/// Including this header
|
||||||
|
pub file_size: u32,
|
||||||
|
/// Number of Scn's
|
||||||
|
#[br(temp)]
|
||||||
|
#[bw(calc = scns.len() as u32)]
|
||||||
|
scn_count: u32,
|
||||||
|
#[br(count = scn_count)]
|
||||||
|
pub scns: Vec<Scn>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[brw(little)]
|
||||||
|
#[brw(magic = b"SCN1")]
|
||||||
|
pub struct Scn {
|
||||||
|
total_size: u32,
|
||||||
|
pub header: ScnHeader,
|
||||||
|
#[br(seek_before = SeekFrom::Current(header.offset_general as i64 - ScnHeader::SIZE as i64))]
|
||||||
|
#[br(restore_position)]
|
||||||
|
pub general: ScnGeneralSection,
|
||||||
|
#[br(seek_before = SeekFrom::Current(header.offset_unk1 as i64 - ScnHeader::SIZE as i64))]
|
||||||
|
#[br(restore_position)]
|
||||||
|
pub unk1: ScnUnknown1Section,
|
||||||
|
#[br(seek_before = SeekFrom::Current(header.offset_unk2 as i64 - ScnHeader::SIZE as i64))]
|
||||||
|
#[br(restore_position)]
|
||||||
|
pub unk2: ScnUnknown2Section,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binrw::parser(reader)]
|
||||||
|
pub(crate) fn strings_from_offsets(offsets: &Vec<i32>) -> BinResult<Vec<String>> {
|
||||||
|
let base_offset = reader.stream_position()?;
|
||||||
|
|
||||||
|
let mut strings: Vec<String> = vec![];
|
||||||
|
|
||||||
|
for offset in offsets {
|
||||||
|
let string_offset = *offset as u64;
|
||||||
|
|
||||||
|
let mut string = String::new();
|
||||||
|
|
||||||
|
reader.seek(SeekFrom::Start(base_offset + string_offset))?;
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[brw(little)]
|
||||||
|
pub struct ScnHeader {
|
||||||
|
/// offset to FileLayerGroupHeader[NumEmbeddedLayerGroups]
|
||||||
|
offset_embedded_layer_groups: i32,
|
||||||
|
num_embedded_layer_groups: i32,
|
||||||
|
/// offset to FileSceneGeneral
|
||||||
|
offset_general: i32,
|
||||||
|
/// offset to FileSceneFilterList
|
||||||
|
offset_filters: i32,
|
||||||
|
offset_unk1: i32,
|
||||||
|
/// offset to a list of path offsets (ints)
|
||||||
|
offset_layer_group_resources: i32,
|
||||||
|
num_layer_group_resources: i32,
|
||||||
|
unk2: i32,
|
||||||
|
offset_unk2: i32,
|
||||||
|
unk4: i32,
|
||||||
|
unk5: i32,
|
||||||
|
unk6: i32,
|
||||||
|
unk7: i32,
|
||||||
|
unk8: i32,
|
||||||
|
unk9: i32,
|
||||||
|
unk10: i32,
|
||||||
|
|
||||||
|
#[br(count = num_layer_group_resources)]
|
||||||
|
#[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnHeader::SIZE as i64))]
|
||||||
|
#[br(restore_position)]
|
||||||
|
offset_path_layer_group_resources: Vec<i32>,
|
||||||
|
|
||||||
|
#[br(parse_with = strings_from_offsets)]
|
||||||
|
#[br(args(&offset_path_layer_group_resources))]
|
||||||
|
#[br(restore_position)]
|
||||||
|
#[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnHeader::SIZE as i64))]
|
||||||
|
pub path_layer_group_resources: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScnHeader {
|
||||||
|
pub const SIZE: usize = 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[br(little)]
|
||||||
|
pub struct ScnGeneralSection {
|
||||||
|
#[br(map = read_bool_from::<i32>)]
|
||||||
|
pub have_layer_groups: bool,
|
||||||
|
offset_path_terrain: i32,
|
||||||
|
offset_env_spaces: i32,
|
||||||
|
num_env_spaces: i32,
|
||||||
|
unk1: i32,
|
||||||
|
offset_path_sky_visibility: i32,
|
||||||
|
unk2: i32,
|
||||||
|
unk3: i32,
|
||||||
|
unk4: i32,
|
||||||
|
unk5: i32,
|
||||||
|
unk6: i32,
|
||||||
|
unk7: i32,
|
||||||
|
unk8: i32,
|
||||||
|
offset_path_lcb: i32,
|
||||||
|
unk10: i32,
|
||||||
|
unk11: i32,
|
||||||
|
unk12: i32,
|
||||||
|
unk13: i32,
|
||||||
|
unk14: i32,
|
||||||
|
unk15: i32,
|
||||||
|
unk16: i32,
|
||||||
|
#[br(map = read_bool_from::<i32>)]
|
||||||
|
pub have_lcbuw: bool,
|
||||||
|
|
||||||
|
#[br(seek_before = SeekFrom::Current(offset_path_terrain as i64 - ScnGeneralSection::SIZE as i64))]
|
||||||
|
#[br(restore_position, parse_with = read_string_until_null)]
|
||||||
|
pub path_terrain: String,
|
||||||
|
|
||||||
|
#[br(seek_before = SeekFrom::Current(offset_path_sky_visibility as i64 - ScnGeneralSection::SIZE as i64))]
|
||||||
|
#[br(restore_position, parse_with = read_string_until_null)]
|
||||||
|
pub path_sky_visibility: String,
|
||||||
|
|
||||||
|
#[br(seek_before = SeekFrom::Current(offset_path_lcb as i64 - ScnGeneralSection::SIZE as i64))]
|
||||||
|
#[br(restore_position, parse_with = read_string_until_null)]
|
||||||
|
pub path_lcb: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScnGeneralSection {
|
||||||
|
pub const SIZE: usize = 0x58;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[br(little)]
|
||||||
|
pub struct ScnUnknown1Section {
|
||||||
|
unk1: i32,
|
||||||
|
unk2: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScnUnknown1Section {
|
||||||
|
pub const SIZE: usize = 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: definitely not correct
|
||||||
|
#[binread]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[br(little)]
|
||||||
|
pub struct ScnUnknown2Section {
|
||||||
|
#[br(dbg)]
|
||||||
|
unk1: i32,
|
||||||
|
unk2: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScnUnknown2Section {
|
||||||
|
pub const SIZE: usize = 0x8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lvb {
|
||||||
|
/// Reads an existing UWB file
|
||||||
|
pub fn from_existing(buffer: ByteSpan) -> Option<Self> {
|
||||||
|
let mut cursor = Cursor::new(buffer);
|
||||||
|
Lvb::read(&mut cursor).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::fs::read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid() {
|
||||||
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
d.push("resources/tests");
|
||||||
|
d.push("random");
|
||||||
|
|
||||||
|
// Feeding it invalid data should not panic
|
||||||
|
Lvb::from_existing(&read(d).unwrap());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue