From 3a137070ecf6df5fce537db90b6f9299166f218c Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 28 Dec 2022 00:27:00 -0500 Subject: [PATCH 01/23] add got and xng support, begin work on col --- src/collision.rs | 137 ++++++++++++++++++++++++++ src/lib.rs | 6 ++ src/model.rs | 249 +++++++++++++++++++++++++++++++++++++++++++++++ src/motion.rs | 46 +++++++++ src/soi.rs | 86 ++++++++++++++-- src/soi_soup.rs | 53 +++++++++- src/test.rs | 148 ++++++++++++++++++---------- 7 files changed, 665 insertions(+), 60 deletions(-) create mode 100644 src/collision.rs create mode 100644 src/model.rs create mode 100644 src/motion.rs diff --git a/src/collision.rs b/src/collision.rs new file mode 100644 index 0000000..8e1cc1a --- /dev/null +++ b/src/collision.rs @@ -0,0 +1,137 @@ +use crate::model::{Vector3, Vector4}; +use binrw::{BinRead, BinReaderExt, BinResult, BinWrite}; + +// Members that are commented out are part of the streaming data section, and need to be merged into the contents of the header data after extraction. + +#[derive(BinRead, BinWrite, PartialEq, Debug)] +#[br(repr = i32)] +#[bw(repr = i32)] +enum CollisionType { + Soultree = 0, + SoultreeHeirarchy, + Rays, + DynamicRays, + RadiusedLine, + Sphere, + Box, + Ecosystem, + FinitePlane, + + StreamingSoultree, + StreamingHeirarchy, + StreamingFinitePlane, +} + +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct ShortVector { + x: i16, + y: i16, + z: i16, +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct TreeFace { + volume: f32, + unk: [Vector3; 2], + type_indices: [i16; 2], +} + +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct TreeFaceLeaf { + dvalue: f32, + vector: ShortVector, + vertices: [i16; 3], +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct SoultreeCollisionObject { + obb_data: [f32; 12], + reverse_collision_mode: i32, + vertex_count: u32, + tree_face_count: u32, + tree_face_leaf_count: u32, + quantized: i32, + + // #[br(if(quantized == 1), count = vertex_count)] + // quantized_tree_vertices: Vec, + + // #[br(if(quantized == 0), count = vertex_count)] + // tree_vertices: Vec, + load_normals: i32, + + // #[br(if(load_normals == 1 && quantized == 1), count = vertex_count)] + // quantized_tree_normals: Vec, + + // #[br(if(load_normals == 1 && quantized == 0), count = vertex_count)] + // tree_normals: Vec, + + // #[br(count = tree_face_count)] + // tree_faces: Vec, + top_tree_face: TreeFace, + + // #[br(count = tree_face_leaf_count)] + // tree_face_leaves: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct FinitePlaneStruct { + local_vertex_bl: Vector3, + local_vertex_br: Vector3, + local_vertex_tl: Vector3, + local_vertex_tr: Vector3, + plane_normal: Vector3, +} + +// todo +// br ignore all the non streaming bits, add them back +// when checking for col type check against non streaming +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +#[br(magic = b"\x00\x00\x04\xD2")] +pub struct CollisionModel { + col_type: [u8; 4], + version: i32, + collision_type: CollisionType, + + #[br(if(collision_type == CollisionType::StreamingSoultree))] + temp_cmt: i32, + #[br(if(collision_type == CollisionType::StreamingSoultree))] + object: SoultreeCollisionObject, + + #[br(if(collision_type == CollisionType::StreamingHeirarchy))] + object_count: i32, + #[br(if(collision_type == CollisionType::StreamingHeirarchy))] + reverse_collision_mode: i32, + // #[br(if(collision_type == CollisionType::StreamingSoultree))] + // temp_cmt_cpy: i32, + #[br(if(collision_type == CollisionType::StreamingHeirarchy), count = object_count)] + objects: Vec, + + #[br(if(collision_type == CollisionType::StreamingFinitePlane))] + plane_count: i32, + #[br(if(collision_type == CollisionType::StreamingFinitePlane))] + half: Vector3, + // #[br(if(collision_type == CollisionType::FinitePlane), count = plane_count)] + // planes: Vec, +} + +#[derive(BinRead, Debug)] +pub struct StreamingCollisionModel { + model_info: crate::ModelInfo, + + #[br(count = model_info.parameter_count)] + pub parameters: Vec, + + collision_model: CollisionModel, +} diff --git a/src/lib.rs b/src/lib.rs index 03c18dd..722496e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,14 @@ +pub use crate::collision::*; +pub use crate::model::*; +pub use crate::motion::*; pub use crate::soi::*; pub use crate::soi_soup::*; pub use crate::str::*; pub use crate::toc::*; +mod collision; +mod model; +mod motion; mod soi; mod soi_soup; mod str; diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..d300edd --- /dev/null +++ b/src/model.rs @@ -0,0 +1,249 @@ +use std::str::Bytes; + +use binrw::{BinRead, BinResult, BinWrite, BinrwNamedArgs}; + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector4 { + x: f32, + y: f32, + z: f32, + w: f32, +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector3 { + x: f32, + y: f32, + z: f32, +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector2 { + x: f32, + y: f32, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct XNGMeshName { + #[br(count = 64)] + name: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct XNGBone { + name: [u8; 128], + matrix: [f32; 16], + bounding_box_center: [f32; 3], + bounding_box_half: [f32; 3], + bounding_box_radius: f32, + parent_index: u32, +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct XNGDeltaBlock { + num_channels: u32, + + #[br(count = 64)] + controller_name: Vec, + + num_vertices: u32, + xyz_bits: u32, + force_unique: u8, + unk: u32, + unk2: u32, + + delta_count: u32, + + #[br(count = delta_count)] + delta_positions: Vec, + + #[br(count = delta_count)] + delta_normals: Vec, + + #[br(count = delta_count)] + delta_indices: Vec, + + #[br(count = num_vertices)] + positions: Vec, + + #[br(count = num_vertices)] + normals: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct StreamingXNGMesh { + pub surface_index: u32, + pub vertex_type: u32, + + #[br(if((vertex_type & 0x2000) == 0x2000))] + pub compression_stuff: Option<[f32; 8]>, + + pub num_texture_coordinate_sets: u8, + pub compressed: u8, + pub streaming: u8, + pub unk: u8, + pub unk2: u8, + pub unk3: u8, + + #[br(count = num_texture_coordinate_sets)] + pub texture_coordinate_sets: Vec, + + pub num_vertices: u16, + pub num_face_indices: u16, + + #[br(if((vertex_type & 0x100) == 0x100))] + pub delta_block: Option, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct XNGLod { + pub auto_lod_value: f32, + pub num_meshes: u32, + + #[br(count = num_meshes)] + pub meshes: Vec, +} + +#[derive(BinRead, Debug)] +#[br(big)] +#[br(magic = b"xgs\0")] +pub struct XNGHeader { + version: i32, + num_bones: u32, + + #[br(count = num_bones)] + bones: Vec, + + num_mesh_names: i32, + + #[br(count = num_mesh_names)] + pub mesh_names: Vec, + + pub num_lod: u8, + skin_animates_flag: u8, + has_weight: u8, + unused: u8, + + #[br(count = num_lod)] + pub lods: Vec, +} + +#[derive(BinRead, Debug)] +pub struct StreamingRenderableModel { + pub model_info: crate::ModelInfo, + + #[br(count = model_info.parameter_count)] + pub parameters: Vec, + + pub streaming_model_header: XNGHeader, +} + +#[derive(BinrwNamedArgs, Clone, Debug)] +pub struct XNGHeaderArgs { + pub streaming_data: Vec, +} + +// This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper XNG file. +// As such, the streaming data must be passed to write_options. +impl BinWrite for XNGHeader { + type Args = XNGHeaderArgs; + + fn write_options( + &self, + writer: &mut W, + options: &binrw::WriteOptions, + args: Self::Args, + ) -> binrw::BinResult<()> { + let magic = b"xng\0".to_vec(); + Vec::write_options(&magic, writer, options, ())?; + i32::write_options(&self.version, writer, options, ())?; + u32::write_options(&self.num_bones, writer, options, ())?; + Vec::write_options(&self.bones, writer, options, ())?; + i32::write_options(&self.num_mesh_names, writer, options, ())?; + Vec::write_options(&self.mesh_names, writer, options, ())?; + u8::write_options(&self.num_lod, writer, options, ())?; + u8::write_options(&self.skin_animates_flag, writer, options, ())?; + u8::write_options(&self.has_weight, writer, options, ())?; + u8::write_options(&self.unused, writer, options, ())?; + + let mut offset_in_data: usize = 0; + + for lod in &self.lods { + f32::write_options(&lod.auto_lod_value, writer, options, ())?; + u32::write_options(&lod.num_meshes, writer, options, ())?; + for mesh in &lod.meshes { + u32::write_options(&mesh.surface_index, writer, options, ())?; + u32::write_options(&mesh.vertex_type, writer, options, ())?; + if let Some(compression_stuff) = mesh.compression_stuff { + compression_stuff.write_options(writer, options, ())?; + } + u8::write_options(&mesh.num_texture_coordinate_sets, writer, options, ())?; + u8::write_options(&mesh.compressed, writer, options, ())?; + u8::write_options(&mesh.streaming, writer, options, ())?; + u8::write_options(&mesh.unk, writer, options, ())?; + u8::write_options(&mesh.unk2, writer, options, ())?; + u8::write_options(&mesh.unk3, writer, options, ())?; + Vec::write_options(&mesh.texture_coordinate_sets, writer, options, ())?; + + u16::write_options(&mesh.num_vertices, writer, options, ())?; + u16::write_options(&mesh.num_face_indices, writer, options, ())?; + + assert!(mesh.streaming == 1); + + let ty = mesh.vertex_type; + let mut offset: usize = mesh.num_face_indices as usize * 2; + if mesh.num_face_indices % 2 == 1 { + offset += 2; + } + if (ty & 0x01) == 0x01 { + offset += (mesh.num_vertices as usize * 12); + } + if (ty & 0x02) == 0x02 { + offset += (mesh.num_vertices as usize * 12); + } + if (ty & 0x08) == 0x08 { + offset += (mesh.num_vertices as usize * 4); + } + if (ty & 0x04) == 0x04 { + offset += (mesh.num_vertices as usize * 8); + } + if (ty & 0x40) == 0x40 { + offset += (mesh.num_vertices as usize * 4); + } + if (ty & 0x1000) == 0x1000 { + offset += (mesh.num_vertices as usize * 32); + } + if (ty & 0x10) == 0x10 { + offset += (mesh.num_vertices as usize * 8); + } + if (ty & 0x4000) == 0x4000 { + offset += (mesh.num_vertices as usize * 8); + } + if (ty & 0x8000) == 0x8000 { + offset += (mesh.num_vertices as usize * 8); + } + if (ty & 0x20) == 0x20 { + offset += (mesh.num_vertices as usize * 12); + } + + let data = (&args.streaming_data[offset_in_data..offset_in_data + offset]).to_vec(); + Vec::write_options(&data, writer, options, ())?; + + offset_in_data += offset; + + if let Some(delta_block) = &mesh.delta_block { + delta_block.write_options(writer, options, ())?; + } + } + } + Ok(()) + } +} diff --git a/src/motion.rs b/src/motion.rs new file mode 100644 index 0000000..9ad867e --- /dev/null +++ b/src/motion.rs @@ -0,0 +1,46 @@ +use binrw::{BinRead, BinReaderExt, BinResult, BinWrite}; + +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +struct MotionPackString { + len: u32, + + #[br(count = len)] + bone_name: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[br(big)] +#[bw(big)] +pub struct StreamingMotionPackHeader { + version: i32, + motion_type: i32, + frame_count: u32, + object_count: u32, + + #[br(count = object_count)] + bone_targets: Vec, + + duration: f32, + rotation_type: u32, + position_type: u32, + num_positions: u32, + num_rotations: u32, + num_camera_infos: u32, + padding: u32, + + #[br(if(version == -8))] + #[bw(ignore)] + unk_bpb_size: u32, + + #[br(if(version == -8), count = unk_bpb_size)] + #[bw(ignore)] + unk_bpb: Vec, +} + +#[derive(BinRead, Debug)] +pub struct StreamingMotionPack { + pub model_info: crate::ModelInfo, + pub header: StreamingMotionPackHeader, +} diff --git a/src/soi.rs b/src/soi.rs index 9fd5065..47b008e 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -1,7 +1,11 @@ use std::fs::File; use std::path::Path; -use binrw::{BinRead, BinReaderExt, BinResult}; +use binrw::{io::SeekFrom, BinRead, BinReaderExt, BinResult, BinWrite}; + +use crate::collision::*; +use crate::model::*; +use crate::motion::*; #[derive(BinRead, PartialEq, Debug)] #[br(repr = i32)] @@ -37,7 +41,7 @@ struct Header { } #[derive(BinRead, Debug)] -struct ModelInfo { +pub struct ModelInfo { flags: i32, position: [f32; 4], look_vector: [f32; 4], @@ -49,17 +53,31 @@ struct ModelInfo { name: [char; 260], zone: i32, - parameter_count: i32, + pub parameter_count: i32, +} + +#[derive(BinRead, Debug)] +pub struct StreamingParameter { + name: [char; 260], + value: [char; 260], } #[derive(BinRead, Debug)] struct StreamingTexture> { model_info: ModelInfo, - // might be something, currently only padding padding: u32, header: TH, } +#[derive(BinRead, Debug)] +struct StaticTexture { + model_info: ModelInfo, + + dds_size: u32, + #[br(count = dds_size)] + header_file: Vec, +} + #[derive(BinRead, Debug)] pub struct Soi> { header: Header, @@ -72,11 +90,20 @@ pub struct Soi> { #[br(count = header.streaming_textures)] streaming_textures: Vec>, - // #[br(count = header.static_textures)] - // static_textures: Vec, - // #[br(count = header.motion_packs)] - // motion_packs: Vec, + #[br(count = header.static_textures)] + static_textures: Vec, + + #[br(count = header.motion_packs)] + motion_packs: Vec, + + //#[br(if(header.flags & 64 == 1))] + //collision_grid_info: StreamingCollisionGridInfo, + #[br(count = header.renderable_models)] + renderable_models: Vec, + + #[br(count = header.collision_models)] + collision_models: Vec, } impl> Soi { @@ -89,6 +116,19 @@ impl> Soi { file.read_be() } + pub fn find_static_texture_header(&self, section_id: u32, component_id: u32) -> Option<&Vec> { + for texture in &self.static_textures { + let model_info = &texture.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&texture.header_file); + } + } + + None + } + pub fn find_texture_header(&self, section_id: u32, component_id: u32) -> Option<&TH> { for texture in &self.streaming_textures { let model_info = &texture.model_info; @@ -101,4 +141,34 @@ impl> Soi { None } + + pub fn find_motion_pack( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StreamingMotionPackHeader> { + for motion_pack in &self.motion_packs { + let model_info = &motion_pack.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&motion_pack.header); + } + } + + None + } + + pub fn find_model(&self, section_id: u32, component_id: u32) -> Option<&XNGHeader> { + for model in &self.renderable_models { + let model_info = &model.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&model.streaming_model_header); + } + } + + None + } } diff --git a/src/soi_soup.rs b/src/soi_soup.rs index 5f9913d..b8a1c3e 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -2,14 +2,14 @@ use std::path::Path; use binrw::{BinRead, BinResult}; -use crate::{ComponentHeader, Section, Soi, Toc}; +use crate::{ComponentHeader, Section, Soi, StreamingMotionPackHeader, Toc, XNGHeader}; -pub struct SoiSoup> { +pub struct SoiSoup> { toc: Toc, soi: Soi, } -impl> SoiSoup { +impl> SoiSoup { pub fn cook(toc_path: &Path, soi_path: &Path) -> BinResult { let toc = Toc::read(toc_path)?; let soi = Soi::read(soi_path)?; @@ -49,6 +49,25 @@ impl> SoiSoup { sum as u32 } + pub fn find_static_texture_header( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&Vec> { + if let Some(header) = self + .soi + .find_static_texture_header(section_id, component_id) + { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self + .soi + .find_static_texture_header(section_id, component_id) + } + pub fn find_texture_header( &self, section_id: u32, @@ -62,4 +81,32 @@ impl> SoiSoup { let (section_id, component_id) = self.toc.find_ids(instance_id)?; self.soi.find_texture_header(section_id, component_id) } + + pub fn find_motion_pack( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&StreamingMotionPackHeader> { + if let Some(header) = self.soi.find_motion_pack(section_id, component_id) { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self.soi.find_motion_pack(section_id, component_id) + } + + pub fn find_model( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&XNGHeader> { + if let Some(header) = self.soi.find_model(section_id, component_id) { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self.soi.find_model(section_id, component_id) + } } diff --git a/src/test.rs b/src/test.rs index cf1c959..7e36b65 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,15 +1,20 @@ -use std::path::Path; +use std::io::Write; +use std::path::{Path, PathBuf}; -use x_flipper_360::{TextureHeader, TextureSign, TextureSize2D}; +use binrw::{BinWrite, WriteOptions}; +use x_flipper_360::*; -use crate::ComponentKind::Texture; -use crate::{ComponentData, SoiSoup, Str}; +use crate::ComponentKind::{self, Texture}; +use crate::{ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { - let toc_path = Path::new("./data/VehicleInfo.x360.toc"); - let soi_path = Path::new("./data/VehicleInfo.x360.soi"); - let str_path = Path::new("data/VehicleInfo.x360.str"); + let toc_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.toc"); + let soi_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.soi"); + let str_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -28,48 +33,93 @@ fn extract() { } fn process_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { - if component.kind != Texture { - return; + if component.kind == ComponentKind::MotionPack { + let header = soup + .find_motion_pack(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.got", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header.write_to(&mut out); + out.write_all(&component.data); + } + + if component.kind == ComponentKind::RenderableModel { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.xng", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + let op = WriteOptions::new(binrw::Endian::Big); + println!( + "{}, {}, {}, {}, {}", + component.path, + component.data.len(), + header.mesh_names.len(), + header.num_lod, + header.lods[0].num_meshes + ); + header.write_options( + &mut out, + &op, + XNGHeaderArgs { + streaming_data: component.data, + }, + ); + + // out.write_all(&component.data); } - let header = match soup.find_texture_header(section_id, component.id, component.instance_id) { - Some(header) => header, - None => panic!("can not find texture header by section and component id nor instance id"), - }; - - let metadata = header.metadata(); - println!("{} {:?}", component.path, metadata.format()); - - // match metadata.format() { - // TextureFormat::Dxt1 => {} - // TextureFormat::Dxt4_5 => {} - // _ => { - // println!("{} {:?}", component.path, metadata.format()); - // - // let texture_size: TextureSize2D = - // TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); - // - // let config = Config { - // width: texture_size.width() as u32 + 1, - // height: texture_size.height() as u32 + 1, - // depth: None, - // pitch: metadata.pitch() as u32, - // tiled: metadata.tiled(), - // packed_mips: metadata.packed_mips(), - // format: Format::RGBA8, - // mipmap_levels: Some(1.max(metadata.max_mip_level() - metadata.min_mip_level()) as u32), - // base_address: metadata.base_address(), - // mip_address: metadata.mip_address(), - // }; - // - // let path = PathBuf::from(format!( - // "./data/out/{}.dds", - // component.path.replace("\\", "/") - // )); - // create_dir_all(path.parent().unwrap()).unwrap(); - // let mut out = File::create(path).unwrap(); - // - // x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); - // } - // } + if component.kind == ComponentKind::Texture { + match soup.find_texture_header(section_id, component.id, component.instance_id) { + Some(header) => { + let metadata = header.metadata(); + println!("{} {:?}", component.path, metadata.format()); + + match metadata.format() { + TextureFormat::Dxt1 => {} + TextureFormat::Dxt4_5 => {} + _ => { + println!("{} {:?}", component.path, metadata.format()); + + let texture_size: TextureSize2D = + TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); + + let config = Config { + width: texture_size.width() as u32 + 1, + height: texture_size.height() as u32 + 1, + depth: None, + pitch: metadata.pitch() as u32, + tiled: metadata.tiled(), + packed_mips: metadata.packed_mips(), + format: Format::RGBA8, + mipmap_levels: Some(1.max(metadata.max_mip_level() - metadata.min_mip_level()) as u32), + base_address: metadata.base_address(), + mip_address: metadata.mip_address(), + }; + + let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.dds", component.path)); + //std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + //let mut out = std::fs::File::create(path).unwrap(); + + //x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); + } + } + } + None => { + match soup.find_static_texture_header(section_id, component.id, component.instance_id) { + Some(static_header) => { + let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.dds", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + out.write_all(static_header).unwrap(); + } + None => panic!("Bruh Moment!"), + } + } + } + } } From 54712576759b7405a0d138693690b2bf7010c766 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 28 Dec 2022 17:00:49 -0500 Subject: [PATCH 02/23] continue work on collision --- src/collision.rs | 513 +++++++++++++++++++++++++++++++++++++++++++---- src/model.rs | 18 +- src/soi.rs | 23 ++- src/soi_soup.rs | 18 +- src/test.rs | 53 +++-- 5 files changed, 553 insertions(+), 72 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index 8e1cc1a..51d271e 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -1,11 +1,10 @@ use crate::model::{Vector3, Vector4}; -use binrw::{BinRead, BinReaderExt, BinResult, BinWrite}; +use binrw::{BinRead, BinReaderExt, BinResult, BinWrite, ReadOptions}; // Members that are commented out are part of the streaming data section, and need to be merged into the contents of the header data after extraction. #[derive(BinRead, BinWrite, PartialEq, Debug)] -#[br(repr = i32)] -#[bw(repr = i32)] +#[brw(repr = i32)] enum CollisionType { Soultree = 0, SoultreeHeirarchy, @@ -22,37 +21,117 @@ enum CollisionType { StreamingFinitePlane, } +impl std::fmt::Display for CollisionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CollisionType::Soultree => write!(f, "Soultree"), + CollisionType::SoultreeHeirarchy => write!(f, "SoultreeHeirarchy"), + CollisionType::Rays => write!(f, "Rays"), + CollisionType::DynamicRays => write!(f, "DynamicRays"), + CollisionType::RadiusedLine => write!(f, "RadiusedLine"), + CollisionType::Sphere => write!(f, "Sphere"), + CollisionType::Box => write!(f, "Box"), + CollisionType::Ecosystem => write!(f, "Ecosystem"), + CollisionType::FinitePlane => write!(f, "FinitePlane"), + CollisionType::StreamingSoultree => write!(f, "StreamingSoultree"), + CollisionType::StreamingHeirarchy => write!(f, "StreamingHeirarchy"), + CollisionType::StreamingFinitePlane => write!(f, "StreamingFinitePlane"), + } + } +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +struct Vector3i16 { + pub x: i16, + pub y: i16, + pub z: i16, +} + #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] -struct ShortVector { - x: i16, - y: i16, - z: i16, +#[brw(big)] +struct Vector4i16 { + pub x: i16, + pub y: i16, + pub z: i16, + pub w: i16, } #[derive(Default, BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] +#[brw(big)] struct TreeFace { volume: f32, - unk: [Vector3; 2], + vectors: [Vector3; 2], + type_indices: [i16; 2], +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +struct StreamingDataTreeFace { + vectors: [Vector4; 2], type_indices: [i16; 2], + volume: f32, + #[brw(pad_after = 20)] + radius: f32, +} + +impl StreamingDataTreeFace { + pub fn to_tree_face(&self) -> TreeFace { + TreeFace { + volume: self.volume, + vectors: [ + Vector3 { + x: self.vectors[0].x, + y: self.vectors[0].y, + z: self.vectors[0].z, + }, + Vector3 { + x: self.vectors[1].x, + y: self.vectors[1].y, + z: self.vectors[1].z, + }, + ], + type_indices: self.type_indices, + } + } } #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] +#[brw(big)] struct TreeFaceLeaf { dvalue: f32, - vector: ShortVector, + vector: Vector3, vertices: [i16; 3], } +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +struct StreamingDataTreeFaceLeaf { + vector: Vector4, + dvalue: f32, + #[brw(pad_after = 6)] + vertices: [i16; 3], +} + +impl StreamingDataTreeFaceLeaf { + pub fn to_tree_face_leaf(&self) -> TreeFaceLeaf { + TreeFaceLeaf { + dvalue: self.dvalue, + vector: Vector3 { + x: self.vector.x, + y: self.vector.y, + z: self.vector.z, + }, + vertices: self.vertices, + } + } +} + #[derive(Default, BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] +#[brw(big)] struct SoultreeCollisionObject { + temp_cmt: i32, + obb_data: [f32; 12], reverse_collision_mode: i32, vertex_count: u32, @@ -61,29 +140,27 @@ struct SoultreeCollisionObject { quantized: i32, // #[br(if(quantized == 1), count = vertex_count)] - // quantized_tree_vertices: Vec, + // quantized_tree_vertices: Vec, // #[br(if(quantized == 0), count = vertex_count)] - // tree_vertices: Vec, + // tree_vertices: Vec, load_normals: i32, // #[br(if(load_normals == 1 && quantized == 1), count = vertex_count)] - // quantized_tree_normals: Vec, + // quantized_tree_normals: Vec, // #[br(if(load_normals == 1 && quantized == 0), count = vertex_count)] - // tree_normals: Vec, + // tree_normals: Vec, // #[br(count = tree_face_count)] // tree_faces: Vec, top_tree_face: TreeFace, - // #[br(count = tree_face_leaf_count)] // tree_face_leaves: Vec, } #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] +#[brw(big)] struct FinitePlaneStruct { local_vertex_bl: Vector3, local_vertex_br: Vector3, @@ -92,20 +169,21 @@ struct FinitePlaneStruct { plane_normal: Vector3, } -// todo -// br ignore all the non streaming bits, add them back -// when checking for col type check against non streaming #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] +#[brw(big)] +struct StreamingHeirarchyEntry { + object_id: i32, + object: SoultreeCollisionObject, +} + +#[derive(BinRead, Debug)] +#[brw(big)] #[br(magic = b"\x00\x00\x04\xD2")] pub struct CollisionModel { col_type: [u8; 4], version: i32, collision_type: CollisionType, - #[br(if(collision_type == CollisionType::StreamingSoultree))] - temp_cmt: i32, #[br(if(collision_type == CollisionType::StreamingSoultree))] object: SoultreeCollisionObject, @@ -113,10 +191,8 @@ pub struct CollisionModel { object_count: i32, #[br(if(collision_type == CollisionType::StreamingHeirarchy))] reverse_collision_mode: i32, - // #[br(if(collision_type == CollisionType::StreamingSoultree))] - // temp_cmt_cpy: i32, #[br(if(collision_type == CollisionType::StreamingHeirarchy), count = object_count)] - objects: Vec, + objects: Vec, #[br(if(collision_type == CollisionType::StreamingFinitePlane))] plane_count: i32, @@ -128,10 +204,373 @@ pub struct CollisionModel { #[derive(BinRead, Debug)] pub struct StreamingCollisionModel { - model_info: crate::ModelInfo, + pub model_info: crate::ModelInfo, #[br(count = model_info.parameter_count)] pub parameters: Vec, - - collision_model: CollisionModel, + + pub collision_model: CollisionModel, +} + +#[derive(binrw::BinrwNamedArgs, Clone, Debug)] +pub struct CollisionModelArgs { + pub streaming_data: Vec, +} + +// This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper GOL file. +// As such, the streaming data must be passed to write_options. +impl BinWrite for CollisionModel { + type Args = CollisionModelArgs; + + fn write_options( + &self, + writer: &mut W, + options: &binrw::WriteOptions, + args: Self::Args, + ) -> binrw::BinResult<()> { + let magic = b"\x00\x00\x04\xD2".to_vec(); + Vec::write_options(&magic, writer, options, ())?; + self.col_type.write_options(writer, options, ())?; + i32::write_options(&self.version, writer, options, ())?; + + let mut offset_in_data: usize = 0; + + let mut cursor = std::io::Cursor::new(&args.streaming_data); + + println!("{}", self.collision_type); + match self.collision_type { + CollisionType::Soultree => panic!("Unsupported collision type!"), + CollisionType::SoultreeHeirarchy => panic!("Unsupported collision type!"), + CollisionType::Rays => panic!("Unsupported collision type!"), + CollisionType::DynamicRays => panic!("Unsupported collision type!"), + CollisionType::RadiusedLine => panic!("Unsupported collision type!"), + CollisionType::Sphere => panic!("Unsupported collision type!"), + CollisionType::Box => panic!("Unsupported collision type!"), + CollisionType::Ecosystem => panic!("Unsupported collision type!"), + CollisionType::FinitePlane => panic!("Unsupported collision type!"), + CollisionType::StreamingSoultree => { + CollisionType::write_options(&CollisionType::Soultree, writer, options, ())?; + + i32::write_options(&self.object.temp_cmt, writer, options, ())?; + self.object.obb_data.write_options(writer, options, ())?; + i32::write_options(&self.object.reverse_collision_mode, writer, options, ())?; + u32::write_options(&self.object.vertex_count, writer, options, ())?; + u32::write_options(&self.object.tree_face_count, writer, options, ())?; + u32::write_options(&self.object.tree_face_leaf_count, writer, options, ())?; + i32::write_options(&self.object.quantized, writer, options, ())?; + + if self.object.quantized == 0 { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 16 * self.object.vertex_count as usize; + } + if self.object.quantized == 1 { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 8 * self.object.vertex_count as usize; + } + i32::write_options(&self.object.load_normals, writer, options, ())?; + if self.object.load_normals == 1 { + if self.object.quantized == 0 { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 16 * self.object.vertex_count as usize; + } + if self.object.quantized == 1 { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 8 * self.object.vertex_count as usize; + } + } + // Nasty hack... + if self.object.vertex_count % 2 == 1 { + offset_in_data += 16; + } + + // Read StreamingDataTreeFaces from the data and write out TreeFaces. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.tree_face_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_faces = Vec::new(); + for vec in vectors.iter() { + tree_faces.push(vec.to_tree_face()); + } + + Vec::write_options(&tree_faces, writer, options, ())?; + offset_in_data += 64 * self.object.tree_face_count as usize; + + // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.tree_face_leaf_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_face_leaves = Vec::new(); + for vec in vectors.iter() { + tree_face_leaves.push(vec.to_tree_face_leaf()); + } + + Vec::write_options(&tree_face_leaves, writer, options, ())?; + offset_in_data += 32 * self.object.tree_face_leaf_count as usize; + // assert_eq!(offset_in_data, args.streaming_data.len()); + } + CollisionType::StreamingHeirarchy => { + CollisionType::write_options(&CollisionType::SoultreeHeirarchy, writer, options, ())?; + + i32::write_options(&self.object_count, writer, options, ())?; + i32::write_options(&self.reverse_collision_mode, writer, options, ())?; + for object in &self.objects { + i32::write_options(&object.object.temp_cmt, writer, options, ())?; + object.object.obb_data.write_options(writer, options, ())?; + i32::write_options(&object.object.reverse_collision_mode, writer, options, ())?; + u32::write_options(&object.object.vertex_count, writer, options, ())?; + u32::write_options(&object.object.tree_face_count, writer, options, ())?; + u32::write_options(&object.object.tree_face_leaf_count, writer, options, ())?; + i32::write_options(&object.object.quantized, writer, options, ())?; + + if object.object.quantized == 0 { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 16 * object.object.vertex_count as usize; + } + if object.object.quantized == 1 { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 8 * object.object.vertex_count as usize; + } + i32::write_options(&object.object.load_normals, writer, options, ())?; + if object.object.load_normals == 1 { + if object.object.quantized == 0 { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 16 * object.object.vertex_count as usize; + } + if object.object.quantized == 1 { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vectors = Vec::new(); + for vec in vectors.iter() { + truncated_vectors.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vectors, writer, options, ())?; + offset_in_data += 8 * object.object.vertex_count as usize; + } + } + // Nasty hack... + if object.object.vertex_count % 2 == 1 { + offset_in_data += 16; + } + + // Read StreamingDataTreeFaces from the data and write out TreeFaces. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_faces = Vec::new(); + for vec in vectors.iter() { + tree_faces.push(vec.to_tree_face()); + } + + Vec::write_options(&tree_faces, writer, options, ())?; + offset_in_data += 64 * object.object.tree_face_count as usize; + + // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. + cursor.set_position(offset_in_data as u64); + let vectors = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_leaf_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_face_leaves = Vec::new(); + for vec in vectors.iter() { + tree_face_leaves.push(vec.to_tree_face_leaf()); + } + + Vec::write_options(&tree_face_leaves, writer, options, ())?; + offset_in_data += 32 * object.object.tree_face_leaf_count as usize; + // assert_eq!(offset_in_data, args.streaming_data.len()); + } + assert_eq!(offset_in_data, args.streaming_data.len()); + } + CollisionType::StreamingFinitePlane => { + CollisionType::write_options(&CollisionType::FinitePlane, writer, options, ())?; + + i32::write_options(&self.plane_count, writer, options, ())?; + Vector3::write_options(&self.half, writer, options, ())?; + + Vec::write_options(&args.streaming_data, writer, options, ())?; + // assert_eq!(self.plane_count as usize * 60, args.streaming_data.len()); + } + } + Ok(()) + } } diff --git a/src/model.rs b/src/model.rs index d300edd..52e655a 100644 --- a/src/model.rs +++ b/src/model.rs @@ -5,25 +5,25 @@ use binrw::{BinRead, BinResult, BinWrite, BinrwNamedArgs}; #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] pub struct Vector4 { - x: f32, - y: f32, - z: f32, - w: f32, + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, } #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] pub struct Vector3 { - x: f32, - y: f32, - z: f32, + pub x: f32, + pub y: f32, + pub z: f32, } #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] pub struct Vector2 { - x: f32, - y: f32, + pub x: f32, + pub y: f32, } #[derive(BinRead, BinWrite, Debug)] diff --git a/src/soi.rs b/src/soi.rs index 47b008e..a3eb1cc 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -97,11 +97,11 @@ pub struct Soi> { #[br(count = header.motion_packs)] motion_packs: Vec, - //#[br(if(header.flags & 64 == 1))] - //collision_grid_info: StreamingCollisionGridInfo, + // #[br(if(header.flags & 64 == 1))] + // collision_grid_info: StreamingCollisionGridInfo, #[br(count = header.renderable_models)] renderable_models: Vec, - + #[br(count = header.collision_models)] collision_models: Vec, } @@ -159,6 +159,23 @@ impl> Soi { None } + pub fn find_collision_model( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&CollisionModel> { + for collision_model in &self.collision_models { + let model_info = &collision_model.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&collision_model.collision_model); + } + } + + None + } + pub fn find_model(&self, section_id: u32, component_id: u32) -> Option<&XNGHeader> { for model in &self.renderable_models { let model_info = &model.model_info; diff --git a/src/soi_soup.rs b/src/soi_soup.rs index b8a1c3e..b143d53 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -2,7 +2,9 @@ use std::path::Path; use binrw::{BinRead, BinResult}; -use crate::{ComponentHeader, Section, Soi, StreamingMotionPackHeader, Toc, XNGHeader}; +use crate::{ + CollisionModel, ComponentHeader, Section, Soi, StreamingMotionPackHeader, Toc, XNGHeader, +}; pub struct SoiSoup> { toc: Toc, @@ -96,6 +98,20 @@ impl> SoiSoup { self.soi.find_motion_pack(section_id, component_id) } + pub fn find_collision_model( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&CollisionModel> { + if let Some(header) = self.soi.find_collision_model(section_id, component_id) { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self.soi.find_collision_model(section_id, component_id) + } + pub fn find_model( &self, section_id: u32, diff --git a/src/test.rs b/src/test.rs index 7e36b65..6813ece 100644 --- a/src/test.rs +++ b/src/test.rs @@ -5,7 +5,7 @@ use binrw::{BinWrite, WriteOptions}; use x_flipper_360::*; use crate::ComponentKind::{self, Texture}; -use crate::{ComponentData, SoiSoup, Str, XNGHeaderArgs}; +use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { @@ -53,38 +53,47 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.xng", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - let op = WriteOptions::new(binrw::Endian::Big); - println!( - "{}, {}, {}, {}, {}", - component.path, - component.data.len(), - header.mesh_names.len(), - header.num_lod, - header.lods[0].num_meshes - ); - header.write_options( - &mut out, - &op, - XNGHeaderArgs { - streaming_data: component.data, - }, - ); - - // out.write_all(&component.data); + let options = WriteOptions::new(binrw::Endian::Big); + header + .write_options( + &mut out, + &options, + XNGHeaderArgs { + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } + + if component.kind == ComponentKind::CollisionModel { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.gol", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + let options = WriteOptions::new(binrw::Endian::Big); + header + .write_options( + &mut out, + &options, + CollisionModelArgs { + streaming_data: component.data.clone(), + }, + ) + .unwrap(); } if component.kind == ComponentKind::Texture { match soup.find_texture_header(section_id, component.id, component.instance_id) { Some(header) => { let metadata = header.metadata(); - println!("{} {:?}", component.path, metadata.format()); match metadata.format() { TextureFormat::Dxt1 => {} TextureFormat::Dxt4_5 => {} _ => { - println!("{} {:?}", component.path, metadata.format()); - let texture_size: TextureSize2D = TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); From a1c50ddd638ec0d3fa3b86cef307d0da097f5c3a Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 29 Dec 2022 15:17:20 -0500 Subject: [PATCH 03/23] add fmt::display impls to some component types --- src/collision.rs | 37 +++++++------- src/model.rs | 41 +++++++-------- src/motion.rs | 2 +- src/soi.rs | 66 ++++++++++++++++--------- src/soi_soup.rs | 30 +++++------ src/test.rs | 126 +++++++++++++++++++++++++++++++++++++---------- src/utils.rs | 74 ++++++++++++++++++++++++++++ 7 files changed, 266 insertions(+), 110 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index 51d271e..4f9fa11 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -1,5 +1,5 @@ -use crate::model::{Vector3, Vector4}; -use binrw::{BinRead, BinReaderExt, BinResult, BinWrite, ReadOptions}; +use crate::utils::*; +use binrw::{BinRead, BinResult, BinWrite, ReadOptions}; // Members that are commented out are part of the streaming data section, and need to be merged into the contents of the header data after extraction. @@ -40,23 +40,6 @@ impl std::fmt::Display for CollisionType { } } -#[derive(BinRead, BinWrite, Debug)] -#[brw(big)] -struct Vector3i16 { - pub x: i16, - pub y: i16, - pub z: i16, -} - -#[derive(BinRead, BinWrite, Debug)] -#[brw(big)] -struct Vector4i16 { - pub x: i16, - pub y: i16, - pub z: i16, - pub w: i16, -} - #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] struct TreeFace { @@ -212,6 +195,22 @@ pub struct StreamingCollisionModel { pub collision_model: CollisionModel, } +impl std::fmt::Display for StreamingCollisionModel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "COL={}\nPosition={}\nLookVector={}\nUpVector={}", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector + ); + Ok(for param in self.parameters.iter() { + write!(f, "{}\n", param); + }) + } +} + #[derive(binrw::BinrwNamedArgs, Clone, Debug)] pub struct CollisionModelArgs { pub streaming_data: Vec, diff --git a/src/model.rs b/src/model.rs index 52e655a..9778e41 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,31 +1,8 @@ use std::str::Bytes; +use crate::utils::*; use binrw::{BinRead, BinResult, BinWrite, BinrwNamedArgs}; -#[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] -pub struct Vector4 { - pub x: f32, - pub y: f32, - pub z: f32, - pub w: f32, -} - -#[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] -pub struct Vector3 { - pub x: f32, - pub y: f32, - pub z: f32, -} - -#[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] -pub struct Vector2 { - pub x: f32, - pub y: f32, -} - #[derive(BinRead, BinWrite, Debug)] #[brw(big)] pub struct XNGMeshName { @@ -146,6 +123,22 @@ pub struct StreamingRenderableModel { pub streaming_model_header: XNGHeader, } +impl std::fmt::Display for StreamingRenderableModel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector + ); + Ok(for param in self.parameters.iter() { + write!(f, "{}\n", param); + }) + } +} + #[derive(BinrwNamedArgs, Clone, Debug)] pub struct XNGHeaderArgs { pub streaming_data: Vec, diff --git a/src/motion.rs b/src/motion.rs index 9ad867e..2eb1019 100644 --- a/src/motion.rs +++ b/src/motion.rs @@ -1,4 +1,4 @@ -use binrw::{BinRead, BinReaderExt, BinResult, BinWrite}; +use binrw::{BinRead, BinResult, BinWrite}; #[derive(BinRead, BinWrite, Debug)] #[br(big)] diff --git a/src/soi.rs b/src/soi.rs index a3eb1cc..de9416f 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -6,6 +6,7 @@ use binrw::{io::SeekFrom, BinRead, BinReaderExt, BinResult, BinWrite}; use crate::collision::*; use crate::model::*; use crate::motion::*; +use crate::utils::*; #[derive(BinRead, PartialEq, Debug)] #[br(repr = i32)] @@ -43,14 +44,14 @@ struct Header { #[derive(BinRead, Debug)] pub struct ModelInfo { flags: i32, - position: [f32; 4], - look_vector: [f32; 4], - up_vector: [f32; 4], - is_animated: i32, + pub position: Vector4, + pub look_vector: Vector4, + pub up_vector: Vector4, + pub is_animated: i32, section_id: i32, component_id: i32, - name: [char; 260], + pub name: [char; 260], zone: i32, pub parameter_count: i32, @@ -62,20 +63,31 @@ pub struct StreamingParameter { value: [char; 260], } +impl std::fmt::Display for StreamingParameter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}={}", + clean_string(&self.name), + clean_string(&self.value) + ) + } +} + #[derive(BinRead, Debug)] -struct StreamingTexture> { - model_info: ModelInfo, - padding: u32, - header: TH, +pub struct StreamingTexture> { + pub model_info: ModelInfo, + pub padding: u32, + pub header: TH, } #[derive(BinRead, Debug)] -struct StaticTexture { - model_info: ModelInfo, +pub struct StaticTexture { + pub model_info: ModelInfo, - dds_size: u32, + pub dds_size: u32, #[br(count = dds_size)] - header_file: Vec, + pub header_file: Vec, } #[derive(BinRead, Debug)] @@ -116,26 +128,30 @@ impl> Soi { file.read_be() } - pub fn find_static_texture_header(&self, section_id: u32, component_id: u32) -> Option<&Vec> { + pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { let model_info = &texture.model_info; if model_info.section_id == section_id as i32 && model_info.component_id == component_id as i32 { - return Some(&texture.header_file); + return Some(&texture); } } None } - pub fn find_texture_header(&self, section_id: u32, component_id: u32) -> Option<&TH> { + pub fn find_streaming_texture( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StreamingTexture> { for texture in &self.streaming_textures { let model_info = &texture.model_info; if model_info.section_id == section_id as i32 && model_info.component_id == component_id as i32 { - return Some(&texture.header); + return Some(&texture); } } @@ -146,13 +162,13 @@ impl> Soi { &self, section_id: u32, component_id: u32, - ) -> Option<&StreamingMotionPackHeader> { + ) -> Option<&StreamingMotionPack> { for motion_pack in &self.motion_packs { let model_info = &motion_pack.model_info; if model_info.section_id == section_id as i32 && model_info.component_id == component_id as i32 { - return Some(&motion_pack.header); + return Some(&motion_pack); } } @@ -163,26 +179,30 @@ impl> Soi { &self, section_id: u32, component_id: u32, - ) -> Option<&CollisionModel> { + ) -> Option<&StreamingCollisionModel> { for collision_model in &self.collision_models { let model_info = &collision_model.model_info; if model_info.section_id == section_id as i32 && model_info.component_id == component_id as i32 { - return Some(&collision_model.collision_model); + return Some(&collision_model); } } None } - pub fn find_model(&self, section_id: u32, component_id: u32) -> Option<&XNGHeader> { + pub fn find_model( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StreamingRenderableModel> { for model in &self.renderable_models { let model_info = &model.model_info; if model_info.section_id == section_id as i32 && model_info.component_id == component_id as i32 { - return Some(&model.streaming_model_header); + return Some(&model); } } diff --git a/src/soi_soup.rs b/src/soi_soup.rs index b143d53..a275ab9 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -3,7 +3,8 @@ use std::path::Path; use binrw::{BinRead, BinResult}; use crate::{ - CollisionModel, ComponentHeader, Section, Soi, StreamingMotionPackHeader, Toc, XNGHeader, + ComponentHeader, Section, Soi, StaticTexture, StreamingCollisionModel, StreamingMotionPack, + StreamingRenderableModel, StreamingTexture, Toc, }; pub struct SoiSoup> { @@ -51,37 +52,32 @@ impl> SoiSoup { sum as u32 } - pub fn find_static_texture_header( + pub fn find_static_texture( &self, section_id: u32, component_id: u32, instance_id: u32, - ) -> Option<&Vec> { - if let Some(header) = self - .soi - .find_static_texture_header(section_id, component_id) - { + ) -> Option<&StaticTexture> { + if let Some(header) = self.soi.find_static_texture(section_id, component_id) { return Some(header); } let (section_id, component_id) = self.toc.find_ids(instance_id)?; - self - .soi - .find_static_texture_header(section_id, component_id) + self.soi.find_static_texture(section_id, component_id) } - pub fn find_texture_header( + pub fn find_streaming_texture( &self, section_id: u32, component_id: u32, instance_id: u32, - ) -> Option<&TH> { - if let Some(header) = self.soi.find_texture_header(section_id, component_id) { + ) -> Option<&StreamingTexture> { + if let Some(header) = self.soi.find_streaming_texture(section_id, component_id) { return Some(header); } let (section_id, component_id) = self.toc.find_ids(instance_id)?; - self.soi.find_texture_header(section_id, component_id) + self.soi.find_streaming_texture(section_id, component_id) } pub fn find_motion_pack( @@ -89,7 +85,7 @@ impl> SoiSoup { section_id: u32, component_id: u32, instance_id: u32, - ) -> Option<&StreamingMotionPackHeader> { + ) -> Option<&StreamingMotionPack> { if let Some(header) = self.soi.find_motion_pack(section_id, component_id) { return Some(header); } @@ -103,7 +99,7 @@ impl> SoiSoup { section_id: u32, component_id: u32, instance_id: u32, - ) -> Option<&CollisionModel> { + ) -> Option<&StreamingCollisionModel> { if let Some(header) = self.soi.find_collision_model(section_id, component_id) { return Some(header); } @@ -117,7 +113,7 @@ impl> SoiSoup { section_id: u32, component_id: u32, instance_id: u32, - ) -> Option<&XNGHeader> { + ) -> Option<&StreamingRenderableModel> { if let Some(header) = self.soi.find_model(section_id, component_id) { return Some(header); } diff --git a/src/test.rs b/src/test.rs index 6813ece..6c935f1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,17 +4,17 @@ use std::path::{Path, PathBuf}; use binrw::{BinWrite, WriteOptions}; use x_flipper_360::*; -use crate::ComponentKind::{self, Texture}; +use crate::ComponentKind::{self, *}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.toc"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.soi"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\HUB\\HUB.x360.str"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -32,16 +32,69 @@ fn extract() { } } +#[test] +fn dump_scn() { + let toc_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.toc"); + let soi_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.soi"); + let str_path = + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.str"); + + let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); + let mut str = Str::read(str_path).unwrap(); + for (id, section) in soup.find_sections().iter().enumerate() { + let section_data = str.read_section_data(section).unwrap(); + + for component in section_data.uncached { + print_component(&soup, id as u32, component); + } + + for component in section_data.cached { + print_component(&soup, id as u32, component); + } + } +} + +fn print_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { + match component.kind { + RenderableModel => { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + println!("{}", header); + } + Texture => { + // println!("found Texture component kind; skipping..."); + } + CollisionModel => { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + println!("{}", header); + } + UserData => { + // println!("found UserData component kind; skipping..."); + } + MotionPack => { + // println!("found MotionPack component kind; skipping..."); + } + CollisionGrid => { + // println!("found CollisionGrid component kind; skipping..."); + } + } +} + fn process_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.got", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header.write_to(&mut out); + header.header.write_to(&mut out); out.write_all(&component.data); } @@ -50,11 +103,12 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.xng", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.xng", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); header + .streaming_model_header .write_options( &mut out, &options, @@ -70,11 +124,12 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.gol", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.gol", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); header + .collision_model .write_options( &mut out, &options, @@ -86,13 +141,35 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: } if component.kind == ComponentKind::Texture { - match soup.find_texture_header(section_id, component.id, component.instance_id) { + match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(header) => { - let metadata = header.metadata(); + let metadata = header.header.metadata(); match metadata.format() { - TextureFormat::Dxt1 => {} - TextureFormat::Dxt4_5 => {} + TextureFormat::Dxt1 => { + let texture_size: TextureSize2D = + TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); + + let config = Config { + width: texture_size.width() as u32 + 1, + height: texture_size.height() as u32 + 1, + depth: None, + pitch: metadata.pitch() as u32, + tiled: metadata.tiled(), + packed_mips: metadata.packed_mips(), + format: Format::Dxt1, + mipmap_levels: Some(1.max(metadata.max_mip_level() - metadata.min_mip_level()) as u32), + base_address: metadata.base_address(), + mip_address: metadata.mip_address(), + }; + + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + println!("{}", component.data.len()); + x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); + } + // TextureFormat::Dxt4_5 => {} _ => { let texture_size: TextureSize2D = TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); @@ -110,25 +187,22 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.dds", component.path)); - //std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - //let mut out = std::fs::File::create(path).unwrap(); - - //x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); - } - } - } - None => { - match soup.find_static_texture_header(section_id, component.id, component.instance_id) { - Some(static_header) => { - let path = PathBuf::from(format!("D:\\GigaLeak\\asdjkl\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - out.write_all(static_header).unwrap(); + x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); } - None => panic!("Bruh Moment!"), } } + None => match soup.find_static_texture(section_id, component.id, component.instance_id) { + Some(static_texture) => { + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + out.write_all(&static_texture.header_file).unwrap(); + } + None => panic!("Failed to find texture header."), + }, } } } diff --git a/src/utils.rs b/src/utils.rs index 8a3720a..47f7da6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,64 @@ +use binrw::{BinRead, BinWrite}; + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector4 { + pub x: f32, + pub y: f32, + pub z: f32, + pub w: f32, +} + +impl std::fmt::Display for Vector4 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},{},{},{}", self.x, self.y, self.z, self.w) + } +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector3 { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl std::fmt::Display for Vector3 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},{},{}", self.x, self.y, self.z) + } +} + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector2 { + pub x: f32, + pub y: f32, +} + +impl std::fmt::Display for Vector2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},{}", self.x, self.y) + } +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector3i16 { + pub x: i16, + pub y: i16, + pub z: i16, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Vector4i16 { + pub x: i16, + pub y: i16, + pub z: i16, + pub w: i16, +} + const NULL_BYTE: char = '\0'; const BACKSLASH: char = '\\'; const SLASH: char = '/'; @@ -19,3 +80,16 @@ pub(crate) fn clean_path(input: &[char]) -> String { output } + +pub(crate) fn clean_string(input: &[char]) -> String { + let mut output = String::new(); + + for c in input { + if c == &NULL_BYTE { + return output; + } + output.push(*c) + } + + output +} From 2a83712c66b16e792ae2510884adf4e3473c9278 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 29 Dec 2022 15:37:06 -0500 Subject: [PATCH 04/23] tidy up .scn generation test --- src/collision.rs | 2 +- src/model.rs | 2 +- src/test.rs | 21 ++++++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index 4f9fa11..f9aa618 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -199,7 +199,7 @@ impl std::fmt::Display for StreamingCollisionModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "COL={}\nPosition={}\nLookVector={}\nUpVector={}", + "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\n", clean_string(&self.model_info.name), self.model_info.position, self.model_info.look_vector, diff --git a/src/model.rs b/src/model.rs index 9778e41..76c2997 100644 --- a/src/model.rs +++ b/src/model.rs @@ -127,7 +127,7 @@ impl std::fmt::Display for StreamingRenderableModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}", + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", clean_string(&self.model_info.name), self.model_info.position, self.model_info.look_vector, diff --git a/src/test.rs b/src/test.rs index 6c935f1..420aa85 100644 --- a/src/test.rs +++ b/src/test.rs @@ -43,26 +43,36 @@ fn dump_scn() { let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); + let mut num_anim_models = 1; + let mut num_static_models = 1; + let mut num_objects = 1; for (id, section) in soup.find_sections().iter().enumerate() { let section_data = str.read_section_data(section).unwrap(); for component in section_data.uncached { - print_component(&soup, id as u32, component); + print_component(&soup, id as u32, component, &mut num_anim_models, &mut num_static_models, &mut num_objects); } for component in section_data.cached { - print_component(&soup, id as u32, component); + print_component(&soup, id as u32, component, &mut num_anim_models, &mut num_static_models, &mut num_objects); } } } -fn print_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { +fn print_component(soup: &SoiSoup, section_id: u32, component: ComponentData, num_anim_models: &mut i32, num_static_models: &mut i32, num_objects: &mut i32) { match component.kind { RenderableModel => { let header = soup .find_model(section_id, component.id, component.instance_id) .unwrap(); - println!("{}", header); + if header.model_info.is_animated == 1{ + println!("[AnimatedModel{}]\n{}", num_anim_models, header); + *num_anim_models = *num_anim_models + 1; + } + else { + println!("[Model{}]\n{}", num_static_models, header); + *num_static_models = *num_static_models + 1; + } } Texture => { // println!("found Texture component kind; skipping..."); @@ -71,7 +81,8 @@ fn print_component(soup: &SoiSoup, section_id: u32, component: Co let header = soup .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - println!("{}", header); + println!("[Object{}]\n{}", num_objects, header); + *num_objects = *num_objects + 1; } UserData => { // println!("found UserData component kind; skipping..."); From 7b26ccfaffa98f96b50c38ad81e8b9943b6d3955 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 29 Dec 2022 19:38:06 -0500 Subject: [PATCH 05/23] lets gooo cr_03 loads --- src/model.rs | 2 +- src/test.rs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/model.rs b/src/model.rs index 76c2997..1fac91f 100644 --- a/src/model.rs +++ b/src/model.rs @@ -180,7 +180,7 @@ impl BinWrite for XNGHeader { } u8::write_options(&mesh.num_texture_coordinate_sets, writer, options, ())?; u8::write_options(&mesh.compressed, writer, options, ())?; - u8::write_options(&mesh.streaming, writer, options, ())?; + u8::write_options(&0, writer, options, ())?; u8::write_options(&mesh.unk, writer, options, ())?; u8::write_options(&mesh.unk2, writer, options, ())?; u8::write_options(&mesh.unk3, writer, options, ())?; diff --git a/src/test.rs b/src/test.rs index 420aa85..3ab4261 100644 --- a/src/test.rs +++ b/src/test.rs @@ -177,10 +177,30 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - println!("{}", component.data.len()); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); } - // TextureFormat::Dxt4_5 => {} + TextureFormat::Dxt4_5 => { + let texture_size: TextureSize2D = + TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); + + let config = Config { + width: texture_size.width() as u32 + 1, + height: texture_size.height() as u32 + 1, + depth: None, + pitch: metadata.pitch() as u32, + tiled: metadata.tiled(), + packed_mips: metadata.packed_mips(), + format: Format::Dxt5, + mipmap_levels: Some(1.max(metadata.max_mip_level() - metadata.min_mip_level()) as u32), + base_address: metadata.base_address(), + mip_address: metadata.mip_address(), + }; + + let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); + } _ => { let texture_size: TextureSize2D = TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); From a765fb7945053814c0db076dca21f14b7ab369f1 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Fri, 30 Dec 2022 10:39:39 -0500 Subject: [PATCH 06/23] fix loading uncompressed/version 0x101 packages --- src/soi.rs | 12 ++++++------ src/soi_soup.rs | 2 +- src/str.rs | 42 ++++++++++++++++++++++++++++-------------- src/test.rs | 30 +++++++++++++++--------------- src/toc.rs | 22 ++++++++++++++-------- src/utils.rs | 22 +++++++++++----------- 6 files changed, 75 insertions(+), 55 deletions(-) diff --git a/src/soi.rs b/src/soi.rs index de9416f..0e458d3 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -18,8 +18,8 @@ enum StreamingMode { } #[derive(BinRead, Debug)] -struct Header { - version: i32, +pub struct Header { + pub version: i32, flags: i32, sections: i32, @@ -51,7 +51,7 @@ pub struct ModelInfo { section_id: i32, component_id: i32, - pub name: [char; 260], + pub name: [u8; 260], zone: i32, pub parameter_count: i32, @@ -59,8 +59,8 @@ pub struct ModelInfo { #[derive(BinRead, Debug)] pub struct StreamingParameter { - name: [char; 260], - value: [char; 260], + name: [u8; 260], + value: [u8; 260], } impl std::fmt::Display for StreamingParameter { @@ -92,7 +92,7 @@ pub struct StaticTexture { #[derive(BinRead, Debug)] pub struct Soi> { - header: Header, + pub header: Header, #[br(count = header.uncached_pages)] uncached_page_sizes: Vec, diff --git a/src/soi_soup.rs b/src/soi_soup.rs index a275ab9..c682c6f 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -14,8 +14,8 @@ pub struct SoiSoup> { impl> SoiSoup { pub fn cook(toc_path: &Path, soi_path: &Path) -> BinResult { - let toc = Toc::read(toc_path)?; let soi = Soi::read(soi_path)?; + let toc = Toc::read(toc_path, soi.header.version == 0x101)?; Ok(Self { toc, soi }) } diff --git a/src/str.rs b/src/str.rs index 8d006b3..de80777 100644 --- a/src/str.rs +++ b/src/str.rs @@ -6,7 +6,7 @@ use binrw::io; use flate2::read::ZlibDecoder; use crate::toc::ComponentKind; -use crate::{ComponentHeader, Section}; +use crate::{ComponentHeader, Section, MemoryEntry}; #[derive(Debug)] pub struct SectionData { @@ -40,22 +40,36 @@ impl Str { pub fn read_section_data(&mut self, section: &Section) -> io::Result { let header = §ion.header; - let zlib = &header.zlib_header; + if let Some(zlib) = &header.zlib_header { + let section_offset = header.memory_entry.offset as u64; + self.file.seek(SeekFrom::Start(section_offset))?; + + let uncached = { + let data = self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; + extract_components(§ion.uncached_components, data) + }; + + let cached = { + let data = self.decode_zlib_data(header.cached_data_size as usize, &zlib.cached_sizes)?; + extract_components(§ion.cached_components, data) + }; + + Ok(SectionData { uncached, cached }) + } + else { + let section_offset = header.memory_entry.offset as u64; + self.file.seek(SeekFrom::Start(section_offset))?; - let section_offset = header.memory_entry.offset as u64; - self.file.seek(SeekFrom::Start(section_offset))?; + let mut uncached_data = vec![0u8; header.uncached_data_size as usize]; + self.file.read(&mut uncached_data); + let uncached = extract_components(§ion.uncached_components, uncached_data); - let uncached = { - let data = self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; - extract_components(§ion.uncached_components, data) - }; + let mut cached_data = vec![0u8; header.cached_data_size as usize]; + self.file.read(&mut cached_data); + let cached = extract_components(§ion.cached_components, cached_data); - let cached = { - let data = self.decode_zlib_data(header.cached_data_size as usize, &zlib.cached_sizes)?; - extract_components(§ion.cached_components, data) - }; - - Ok(SectionData { uncached, cached }) + Ok(SectionData { uncached, cached }) + } } pub fn decode_zlib_data( diff --git a/src/test.rs b/src/test.rs index 3ab4261..1cd7181 100644 --- a/src/test.rs +++ b/src/test.rs @@ -10,12 +10,12 @@ use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.toc"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.soi"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.str"); - + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.str"); + let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -35,11 +35,11 @@ fn extract() { #[test] fn dump_scn() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.toc"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.soi"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\CR_03.x360.str"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -102,10 +102,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.got", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header.header.write_to(&mut out); + header.header.write(&mut out); out.write_all(&component.data); } @@ -114,7 +114,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.xng", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.xng", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -135,7 +135,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.gol", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.gol", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -174,7 +174,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -196,7 +196,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -218,7 +218,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -227,7 +227,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: } None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { - let path = PathBuf::from(format!("D:\\GigaLeak\\CR_03\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); out.write_all(&static_texture.header_file).unwrap(); diff --git a/src/toc.rs b/src/toc.rs index 4cf973a..ed22e87 100644 --- a/src/toc.rs +++ b/src/toc.rs @@ -35,7 +35,7 @@ pub enum ComponentKind { CollisionGrid, } -#[derive(BinRead, Debug)] +#[derive(Default, BinRead, Debug)] pub(crate) struct ZlibHeader { pub(crate) uncached_total_size: i32, pub(crate) cached_total_size: i32, @@ -51,8 +51,9 @@ pub(crate) struct ZlibHeader { } #[derive(BinRead, Debug)] +#[br(import{read_zlib_header: bool})] pub struct SectionHeader { - pub name: [char; 260], + pub name: [u8; 260], pub total_component_count: i32, pub uncached_component_count: i32, @@ -70,12 +71,13 @@ pub struct SectionHeader { pub(crate) uncached_data_size: i32, pub(crate) cached_data_size: i32, - pub(crate) zlib_header: ZlibHeader, + #[br(if(read_zlib_header))] + pub(crate) zlib_header: Option, } #[derive(BinRead, Debug)] pub struct ComponentHeader { - raw_path: [char; 260], + raw_path: [u8; 260], pub instance_id: i32, pub id: i32, @@ -84,7 +86,9 @@ pub struct ComponentHeader { } #[derive(BinRead, Debug)] +#[br(import{read_zlib_header: bool})] pub struct Section { + #[br(args { read_zlib_header: read_zlib_header })] pub header: SectionHeader, #[br(count = header.uncached_component_count)] @@ -100,18 +104,20 @@ pub struct Toc { } impl Toc { - pub fn read(path: &Path) -> BinResult { + pub fn read(path: &Path, is_new: bool) -> BinResult { let mut file = File::open(path)?; - Self::read_file(&mut file) + Self::read_file(&mut file, is_new) } - pub fn read_file(file: &mut File) -> BinResult { + pub fn read_file(file: &mut File, is_new: bool) -> BinResult { let mut sections = Vec::new(); let file_size = file.metadata()?.len(); // read sections until the end of the file is reached while file.stream_position()? < file_size { - let section = file.read_be()?; + // hack that allows newer SOI packages to load + let read_zlib_header = !is_new; + let section = Section::read_be_args(file, binrw::args! {read_zlib_header})?; sections.push(section); } diff --git a/src/utils.rs b/src/utils.rs index 47f7da6..c236136 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,16 +59,16 @@ pub struct Vector4i16 { pub w: i16, } -const NULL_BYTE: char = '\0'; -const BACKSLASH: char = '\\'; -const SLASH: char = '/'; +const NULL_BYTE: u8 = 0; +const BACKSLASH: u8 = 92; +const SLASH: u8 = 47; -pub(crate) fn clean_path(input: &[char]) -> String { - let mut output = String::new(); +pub(crate) fn clean_path(input: &[u8]) -> String { + let mut output = Vec::new(); for c in input { if c == &NULL_BYTE { - return output; + return std::str::from_utf8(&output).unwrap().to_owned(); } if c == &BACKSLASH { @@ -78,18 +78,18 @@ pub(crate) fn clean_path(input: &[char]) -> String { } } - output + std::str::from_utf8(&output).unwrap().to_owned() } -pub(crate) fn clean_string(input: &[char]) -> String { - let mut output = String::new(); +pub(crate) fn clean_string(input: &[u8]) -> String { + let mut output = Vec::new(); for c in input { if c == &NULL_BYTE { - return output; + return std::str::from_utf8(&output).unwrap().to_owned(); } output.push(*c) } - output + std::str::from_utf8(&output).unwrap().to_owned() } From 7f5d9aac0ba795de2b4cda6586915fac446ef07c Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Fri, 30 Dec 2022 15:11:01 -0500 Subject: [PATCH 07/23] update binrw --- Cargo.toml | 2 +- src/model.rs | 20 ++++++++++---------- src/soi.rs | 20 ++++++++++++++++++++ src/soi_soup.rs | 20 ++++++++++++++++++++ src/test.rs | 37 ++++++++++++++++++++++--------------- 5 files changed, 73 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3712ea..6595ab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] modular-bitfield = "0.11" flate2 = "1.0" -binrw = "0.8" +binrw = "0.10.0" [dev-dependencies] x-flipper-360 = { git = "https://github.com/offsetting/x-flipper-360" } diff --git a/src/model.rs b/src/model.rs index 1fac91f..a65e0b0 100644 --- a/src/model.rs +++ b/src/model.rs @@ -197,34 +197,34 @@ impl BinWrite for XNGHeader { offset += 2; } if (ty & 0x01) == 0x01 { - offset += (mesh.num_vertices as usize * 12); + offset += mesh.num_vertices as usize * 12; } if (ty & 0x02) == 0x02 { - offset += (mesh.num_vertices as usize * 12); + offset += mesh.num_vertices as usize * 12; } if (ty & 0x08) == 0x08 { - offset += (mesh.num_vertices as usize * 4); + offset += mesh.num_vertices as usize * 4; } if (ty & 0x04) == 0x04 { - offset += (mesh.num_vertices as usize * 8); + offset += mesh.num_vertices as usize * 8; } if (ty & 0x40) == 0x40 { - offset += (mesh.num_vertices as usize * 4); + offset += mesh.num_vertices as usize * 4; } if (ty & 0x1000) == 0x1000 { - offset += (mesh.num_vertices as usize * 32); + offset += mesh.num_vertices as usize * 32; } if (ty & 0x10) == 0x10 { - offset += (mesh.num_vertices as usize * 8); + offset += mesh.num_vertices as usize * 8; } if (ty & 0x4000) == 0x4000 { - offset += (mesh.num_vertices as usize * 8); + offset += mesh.num_vertices as usize * 8; } if (ty & 0x8000) == 0x8000 { - offset += (mesh.num_vertices as usize * 8); + offset += mesh.num_vertices as usize * 8; } if (ty & 0x20) == 0x20 { - offset += (mesh.num_vertices as usize * 12); + offset += mesh.num_vertices as usize * 12; } let data = (&args.streaming_data[offset_in_data..offset_in_data + offset]).to_vec(); diff --git a/src/soi.rs b/src/soi.rs index 0e458d3..5165200 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -128,6 +128,26 @@ impl> Soi { file.read_be() } + pub fn get_streaming_textures(&self) -> &[StreamingTexture] { + return &self.streaming_textures; + } + + pub fn get_static_textures(&self) -> &[StaticTexture] { + return &self.static_textures; + } + + pub fn get_motion_packs(&self) -> &[StreamingMotionPack] { + return &self.motion_packs; + } + + pub fn get_renderable_models(&self) -> &[StreamingRenderableModel] { + return &self.renderable_models; + } + + pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { + return &self.collision_models; + } + pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { let model_info = &texture.model_info; diff --git a/src/soi_soup.rs b/src/soi_soup.rs index c682c6f..861b6e4 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -42,6 +42,26 @@ impl> SoiSoup { components } + pub fn streaming_textures(&self) -> &[StreamingTexture] { + self.soi.get_streaming_textures() + } + + pub fn static_textures(&self) -> &[StaticTexture] { + self.soi.get_static_textures() + } + + pub fn motion_packs(&self) -> &[StreamingMotionPack] { + self.soi.get_motion_packs() + } + + pub fn renderable_models(&self) -> &[StreamingRenderableModel] { + self.soi.get_renderable_models() + } + + pub fn collision_models(&self) -> &[StreamingCollisionModel] { + self.soi.get_collision_models() + } + pub fn component_count(&self) -> u32 { let mut sum = 0; diff --git a/src/test.rs b/src/test.rs index 1cd7181..5bb3d7d 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,17 +4,18 @@ use std::path::{Path, PathBuf}; use binrw::{BinWrite, WriteOptions}; use x_flipper_360::*; +use crate::utils::*; use crate::ComponentKind::{self, *}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.toc"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.soi"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.str"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -30,16 +31,24 @@ fn extract() { process_component(&soup, id as u32, component); } } + + for static_texture in soup.static_textures().iter() { + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", clean_path(&static_texture.model_info.name))); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + out.write_all(&static_texture.header_file).unwrap(); + } + } #[test] fn dump_scn() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.toc"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.soi"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_03\\MiniMap\\MiniMap.x360.str"); + Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -102,7 +111,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.got", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header.header.write(&mut out); @@ -114,7 +123,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.xng", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.xng", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -129,13 +138,12 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: ) .unwrap(); } - if component.kind == ComponentKind::CollisionModel { let header = soup .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.gol", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.gol", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -150,7 +158,6 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: ) .unwrap(); } - if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(header) => { @@ -174,7 +181,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -196,7 +203,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -218,7 +225,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -227,7 +234,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: } None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { - let path = PathBuf::from(format!("D:\\GigaLeak\\MiniMap\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); out.write_all(&static_texture.header_file).unwrap(); From 8fe1fedf435e96a806d0bee377db6afea0d8469b Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:12:09 -0500 Subject: [PATCH 08/23] begin adding mn support --- src/collision.rs | 630 +++++++++++++++++++++++++++++++++-------------- src/lib.rs | 1 + src/soi.rs | 2 +- src/soi_soup.rs | 2 +- src/str.rs | 14 +- src/test.rs | 119 +++++++-- src/utils.rs | 6 +- 7 files changed, 550 insertions(+), 224 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index f9aa618..fa54f3c 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -50,7 +50,7 @@ struct TreeFace { #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] -struct StreamingDataTreeFace { +struct RaceORamaStreamingDataTreeFace { vectors: [Vector4; 2], type_indices: [i16; 2], volume: f32, @@ -58,7 +58,7 @@ struct StreamingDataTreeFace { radius: f32, } -impl StreamingDataTreeFace { +impl RaceORamaStreamingDataTreeFace { pub fn to_tree_face(&self) -> TreeFace { TreeFace { volume: self.volume, @@ -79,6 +79,25 @@ impl StreamingDataTreeFace { } } +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(big)] +struct StreamingDataTreeFace { + volume: f32, + radius: f32, + type_indices: [i16; 2], + vectors: [Vector3; 2], +} + +impl StreamingDataTreeFace { + pub fn to_tree_face(&self) -> TreeFace { + TreeFace { + volume: self.volume, + vectors: self.vectors, + type_indices: self.type_indices, + } + } +} + #[derive(BinRead, BinWrite, Debug)] #[brw(big)] struct TreeFaceLeaf { @@ -89,14 +108,14 @@ struct TreeFaceLeaf { #[derive(BinRead, BinWrite, Debug)] #[brw(big)] -struct StreamingDataTreeFaceLeaf { +struct RaceORamaStreamingDataTreeFaceLeaf { vector: Vector4, dvalue: f32, #[brw(pad_after = 6)] vertices: [i16; 3], } -impl StreamingDataTreeFaceLeaf { +impl RaceORamaStreamingDataTreeFaceLeaf { pub fn to_tree_face_leaf(&self) -> TreeFaceLeaf { TreeFaceLeaf { dvalue: self.dvalue, @@ -110,6 +129,38 @@ impl StreamingDataTreeFaceLeaf { } } +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +struct StreamingDataTreeFaceLeaf { + vertices: [i16; 3], + unknown1: f32, + unknown2: u16, + vector: Vector3i16, + unknown1_bits: u16, +} +// assert_eq!(std::mem::size_of, 20); + +impl StreamingDataTreeFaceLeaf { + pub fn to_tree_face_leaf(&self, global_vertices: &[Vector3]) -> TreeFaceLeaf { + let vertex = global_vertices.get(self.vertices[0] as usize).unwrap(); + let normal = Vector3 { + x: self.vector.x as f32 / 16384.0_f32, + y: self.vector.y as f32 / 16384.0_f32, + z: self.vector.z as f32 / 16384.0_f32, + }; + let dvalue = -1.0_f32 * (vertex.x * normal.x + vertex.y * normal.y + vertex.z * normal.z); + TreeFaceLeaf { + dvalue, + vector: normal, + vertices: [ + self.vertices[0] as i16, + self.vertices[1] as i16, + self.vertices[2] as i16, + ], + } + } +} + #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] struct SoultreeCollisionObject { @@ -213,6 +264,7 @@ impl std::fmt::Display for StreamingCollisionModel { #[derive(binrw::BinrwNamedArgs, Clone, Debug)] pub struct CollisionModelArgs { + pub ror: bool, pub streaming_data: Vec, } @@ -236,7 +288,6 @@ impl BinWrite for CollisionModel { let mut cursor = std::io::Cursor::new(&args.streaming_data); - println!("{}", self.collision_type); match self.collision_type { CollisionType::Soultree => panic!("Unsupported collision type!"), CollisionType::SoultreeHeirarchy => panic!("Unsupported collision type!"), @@ -258,60 +309,14 @@ impl BinWrite for CollisionModel { u32::write_options(&self.object.tree_face_leaf_count, writer, options, ())?; i32::write_options(&self.object.quantized, writer, options, ())?; - if self.object.quantized == 0 { - // Read Vector4s from the data and write out Vector3s, truncating the W component. - cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( - &mut cursor, - &ReadOptions::new(binrw::Endian::Big), - binrw::VecArgs::builder() - .count(self.object.vertex_count as usize) - .finalize(), - ) - .unwrap(); - - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3 { - x: vec.x, - y: vec.y, - z: vec.z, - }); - } - - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 16 * self.object.vertex_count as usize; - } - if self.object.quantized == 1 { - // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. - cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( - &mut cursor, - &ReadOptions::new(binrw::Endian::Big), - binrw::VecArgs::builder() - .count(self.object.vertex_count as usize) - .finalize(), - ) - .unwrap(); - - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3i16 { - x: vec.x, - y: vec.y, - z: vec.z, - }); - } + // Conversion from MN's streaming format requires the vertices be cached and used later for DValue calculation. + let mut global_vertices = Vec::new(); - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 8 * self.object.vertex_count as usize; - } - i32::write_options(&self.object.load_normals, writer, options, ())?; - if self.object.load_normals == 1 { - if self.object.quantized == 0 { + if self.object.quantized == 0 { + if args.ror { // Read Vector4s from the data and write out Vector3s, truncating the W component. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let vertices = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() @@ -320,22 +325,20 @@ impl BinWrite for CollisionModel { ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3 { + let mut truncated_vertices = Vec::new(); + for vec in vertices.iter() { + truncated_vertices.push(Vector3 { x: vec.x, y: vec.y, z: vec.z, }); } - Vec::write_options(&truncated_vectors, writer, options, ())?; + Vec::write_options(&truncated_vertices, writer, options, ())?; offset_in_data += 16 * self.object.vertex_count as usize; - } - if self.object.quantized == 1 { - // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + } else { cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + global_vertices = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() @@ -344,222 +347,479 @@ impl BinWrite for CollisionModel { ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3i16 { - x: vec.x, - y: vec.y, - z: vec.z, - }); - } + Vec::write_options(&global_vertices, writer, options, ())?; + offset_in_data += 12 * self.object.vertex_count as usize; - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 8 * self.object.vertex_count as usize; + // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]).to_vec(); + // Vec::write_options(&data, writer, options, ())?; + // offset_in_data += 12 * self.object.vertex_count as usize; } } - // Nasty hack... - if self.object.vertex_count % 2 == 1 { - offset_in_data += 16; - } - - // Read StreamingDataTreeFaces from the data and write out TreeFaces. - cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( - &mut cursor, - &ReadOptions::new(binrw::Endian::Big), - binrw::VecArgs::builder() - .count(self.object.tree_face_count as usize) - .finalize(), - ) - .unwrap(); - - let mut tree_faces = Vec::new(); - for vec in vectors.iter() { - tree_faces.push(vec.to_tree_face()); - } - - Vec::write_options(&tree_faces, writer, options, ())?; - offset_in_data += 64 * self.object.tree_face_count as usize; - - // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. - cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( - &mut cursor, - &ReadOptions::new(binrw::Endian::Big), - binrw::VecArgs::builder() - .count(self.object.tree_face_leaf_count as usize) - .finalize(), - ) - .unwrap(); - - let mut tree_face_leaves = Vec::new(); - for vec in vectors.iter() { - tree_face_leaves.push(vec.to_tree_face_leaf()); - } - - Vec::write_options(&tree_face_leaves, writer, options, ())?; - offset_in_data += 32 * self.object.tree_face_leaf_count as usize; - // assert_eq!(offset_in_data, args.streaming_data.len()); - } - CollisionType::StreamingHeirarchy => { - CollisionType::write_options(&CollisionType::SoultreeHeirarchy, writer, options, ())?; - - i32::write_options(&self.object_count, writer, options, ())?; - i32::write_options(&self.reverse_collision_mode, writer, options, ())?; - for object in &self.objects { - i32::write_options(&object.object.temp_cmt, writer, options, ())?; - object.object.obb_data.write_options(writer, options, ())?; - i32::write_options(&object.object.reverse_collision_mode, writer, options, ())?; - u32::write_options(&object.object.vertex_count, writer, options, ())?; - u32::write_options(&object.object.tree_face_count, writer, options, ())?; - u32::write_options(&object.object.tree_face_leaf_count, writer, options, ())?; - i32::write_options(&object.object.quantized, writer, options, ())?; - - if object.object.quantized == 0 { - // Read Vector4s from the data and write out Vector3s, truncating the W component. + if self.object.quantized == 1 { + if args.ror { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let vertices = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.vertex_count as usize) + .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3 { + let mut truncated_vertices = Vec::new(); + for vec in vertices.iter() { + truncated_vertices.push(Vector3i16 { x: vec.x, y: vec.y, z: vec.z, }); } - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 16 * object.object.vertex_count as usize; - } - if object.object.quantized == 1 { - // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + Vec::write_options(&truncated_vertices, writer, options, ())?; + offset_in_data += 8 * self.object.vertex_count as usize; + } else { cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let quantized_vertices = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.vertex_count as usize) + .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3i16 { - x: vec.x, - y: vec.y, - z: vec.z, + Vec::write_options(&quantized_vertices, writer, options, ())?; + offset_in_data += 6 * self.object.vertex_count as usize; + + for vec in quantized_vertices.iter() { + global_vertices.push(Vector3 { + x: vec.x as f32 / 16384.0_f32, + y: vec.y as f32 / 16384.0_f32, + z: vec.z as f32 / 16384.0_f32, }); } - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 8 * object.object.vertex_count as usize; + // let data = (&args.streaming_data[offset_in_data..offset_in_data + 6 * self.object.vertex_count as usize]).to_vec(); + // Vec::write_options(&data, writer, options, ())?; + // offset_in_data += 6 * self.object.vertex_count as usize; } - i32::write_options(&object.object.load_normals, writer, options, ())?; - if object.object.load_normals == 1 { - if object.object.quantized == 0 { + } + i32::write_options(&self.object.load_normals, writer, options, ())?; + if self.object.load_normals == 1 { + if self.object.quantized == 0 { + if args.ror { // Read Vector4s from the data and write out Vector3s, truncating the W component. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let normals = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.vertex_count as usize) + .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3 { + let mut truncated_normals = Vec::new(); + for vec in normals.iter() { + truncated_normals.push(Vector3 { x: vec.x, y: vec.y, z: vec.z, }); } - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 16 * object.object.vertex_count as usize; + Vec::write_options(&truncated_normals, writer, options, ())?; + offset_in_data += 16 * self.object.vertex_count as usize; + } else { + let data = (&args.streaming_data + [offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]) + .to_vec(); + Vec::write_options(&data, writer, options, ())?; + offset_in_data += 12 * self.object.vertex_count as usize; } - if object.object.quantized == 1 { + } + if self.object.quantized == 1 { + if args.ror { // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let normals = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.vertex_count as usize) + .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - let mut truncated_vectors = Vec::new(); - for vec in vectors.iter() { - truncated_vectors.push(Vector3i16 { + let mut truncated_normals = Vec::new(); + for vec in normals.iter() { + truncated_normals.push(Vector3i16 { x: vec.x, y: vec.y, z: vec.z, }); } - Vec::write_options(&truncated_vectors, writer, options, ())?; - offset_in_data += 8 * object.object.vertex_count as usize; + Vec::write_options(&truncated_normals, writer, options, ())?; + offset_in_data += 8 * self.object.vertex_count as usize; + } else { + let data = (&args.streaming_data + [offset_in_data..offset_in_data + 6 * self.object.vertex_count as usize]) + .to_vec(); + Vec::write_options(&data, writer, options, ())?; + offset_in_data += 6 * self.object.vertex_count as usize; } } - // Nasty hack... - if object.object.vertex_count % 2 == 1 { - offset_in_data += 16; + } + // Nasty hack... + if args.ror && self.object.vertex_count % 2 == 1 { + offset_in_data += 16; + } + if args.ror { + // Read RaceORamaStreamingDataTreeFace from the data and write out TreeFaces. + cursor.set_position(offset_in_data as u64); + let ror_tree_faces = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.tree_face_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_faces = Vec::new(); + for vec in ror_tree_faces.iter() { + tree_faces.push(vec.to_tree_face()); } + Vec::write_options(&tree_faces, writer, options, ())?; + offset_in_data += 64 * self.object.tree_face_count as usize; + } else { // Read StreamingDataTreeFaces from the data and write out TreeFaces. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let mn_tree_faces = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.tree_face_count as usize) + .count(self.object.tree_face_count as usize) .finalize(), ) .unwrap(); let mut tree_faces = Vec::new(); - for vec in vectors.iter() { + for vec in mn_tree_faces.iter() { tree_faces.push(vec.to_tree_face()); } Vec::write_options(&tree_faces, writer, options, ())?; - offset_in_data += 64 * object.object.tree_face_count as usize; - + offset_in_data += 36 * self.object.tree_face_count as usize; + } + if args.ror { // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. cursor.set_position(offset_in_data as u64); - let vectors = Vec::::read_options( + let ror_face_leaves = Vec::::read_options( &mut cursor, &ReadOptions::new(binrw::Endian::Big), binrw::VecArgs::builder() - .count(object.object.tree_face_leaf_count as usize) + .count(self.object.tree_face_leaf_count as usize) .finalize(), ) .unwrap(); let mut tree_face_leaves = Vec::new(); - for vec in vectors.iter() { + for vec in ror_face_leaves.iter() { tree_face_leaves.push(vec.to_tree_face_leaf()); } Vec::write_options(&tree_face_leaves, writer, options, ())?; - offset_in_data += 32 * object.object.tree_face_leaf_count as usize; - // assert_eq!(offset_in_data, args.streaming_data.len()); + offset_in_data += 32 * self.object.tree_face_leaf_count as usize; + } else { + // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. + cursor.set_position(offset_in_data as u64); + let mn_face_leaves = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(self.object.tree_face_leaf_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_face_leaves = Vec::new(); + for vec in mn_face_leaves.iter() { + tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); + } + + Vec::write_options(&tree_face_leaves, writer, options, ())?; + offset_in_data += 20 * self.object.tree_face_leaf_count as usize; } assert_eq!(offset_in_data, args.streaming_data.len()); } + CollisionType::StreamingHeirarchy => { + CollisionType::write_options(&CollisionType::SoultreeHeirarchy, writer, options, ())?; + + i32::write_options(&self.object_count, writer, options, ())?; + i32::write_options(&self.reverse_collision_mode, writer, options, ())?; + for object in &self.objects { + i32::write_options(&object.object.temp_cmt, writer, options, ())?; + object.object.obb_data.write_options(writer, options, ())?; + i32::write_options(&object.object.reverse_collision_mode, writer, options, ())?; + u32::write_options(&object.object.vertex_count, writer, options, ())?; + u32::write_options(&object.object.tree_face_count, writer, options, ())?; + u32::write_options(&object.object.tree_face_leaf_count, writer, options, ())?; + i32::write_options(&object.object.quantized, writer, options, ())?; + + // Conversion from MN's streaming format requires the vertices be cached and used later for DValue calculation. + let mut global_vertices = Vec::new(); + + if object.object.quantized == 0 { + if args.ror { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vertices = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vertices = Vec::new(); + for vec in vertices.iter() { + truncated_vertices.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vertices, writer, options, ())?; + offset_in_data += 16 * object.object.vertex_count as usize; + } else { + cursor.set_position(offset_in_data as u64); + global_vertices = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + Vec::write_options(&global_vertices, writer, options, ())?; + offset_in_data += 12 * object.object.vertex_count as usize; + + // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]).to_vec(); + // Vec::write_options(&data, writer, options, ())?; + // offset_in_data += 12 * object.object.vertex_count as usize; + } + } + if object.object.quantized == 1 { + if args.ror { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let vertices = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_vertices = Vec::new(); + for vec in vertices.iter() { + truncated_vertices.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_vertices, writer, options, ())?; + offset_in_data += 8 * object.object.vertex_count as usize; + } else { + cursor.set_position(offset_in_data as u64); + let quantized_vertices = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + Vec::write_options(&quantized_vertices, writer, options, ())?; + offset_in_data += 6 * object.object.vertex_count as usize; + + for vec in quantized_vertices.iter() { + global_vertices.push(Vector3 { + x: vec.x as f32 / 16384.0_f32, + y: vec.y as f32 / 16384.0_f32, + z: vec.z as f32 / 16384.0_f32, + }); + } + + // let data = (&args.streaming_data[offset_in_data..offset_in_data + 6 * object.object.vertex_count as usize]).to_vec(); + // Vec::write_options(&data, writer, options, ())?; + // offset_in_data += 6 * object.object.vertex_count as usize; + } + } + i32::write_options(&object.object.load_normals, writer, options, ())?; + if object.object.load_normals == 1 { + if object.object.quantized == 0 { + if args.ror { + // Read Vector4s from the data and write out Vector3s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let normals = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_normals = Vec::new(); + for vec in normals.iter() { + truncated_normals.push(Vector3 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_normals, writer, options, ())?; + offset_in_data += 16 * object.object.vertex_count as usize; + } else { + let data = (&args.streaming_data + [offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]) + .to_vec(); + Vec::write_options(&data, writer, options, ())?; + offset_in_data += 12 * object.object.vertex_count as usize; + } + } + if object.object.quantized == 1 { + if args.ror { + // Read Vector4i16s from the data and write out Vector3i16s, truncating the W component. + cursor.set_position(offset_in_data as u64); + let normals = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.vertex_count as usize) + .finalize(), + ) + .unwrap(); + + let mut truncated_normals = Vec::new(); + for vec in normals.iter() { + truncated_normals.push(Vector3i16 { + x: vec.x, + y: vec.y, + z: vec.z, + }); + } + + Vec::write_options(&truncated_normals, writer, options, ())?; + offset_in_data += 8 * object.object.vertex_count as usize; + } else { + let data = (&args.streaming_data + [offset_in_data..offset_in_data + 6 * object.object.vertex_count as usize]) + .to_vec(); + Vec::write_options(&data, writer, options, ())?; + offset_in_data += 6 * object.object.vertex_count as usize; + } + } + } + // Nasty hack... + if args.ror && object.object.vertex_count % 2 == 1 { + offset_in_data += 16; + } + if args.ror { + // Read RaceORamaStreamingDataTreeFace from the data and write out TreeFaces. + cursor.set_position(offset_in_data as u64); + let ror_tree_faces = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_faces = Vec::new(); + for vec in ror_tree_faces.iter() { + tree_faces.push(vec.to_tree_face()); + } + + Vec::write_options(&tree_faces, writer, options, ())?; + offset_in_data += 64 * object.object.tree_face_count as usize; + } else { + // Read StreamingDataTreeFaces from the data and write out TreeFaces. + cursor.set_position(offset_in_data as u64); + let mn_tree_faces = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_faces = Vec::new(); + for vec in mn_tree_faces.iter() { + tree_faces.push(vec.to_tree_face()); + } + + Vec::write_options(&tree_faces, writer, options, ())?; + offset_in_data += 36 * object.object.tree_face_count as usize; + } + if args.ror { + // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. + cursor.set_position(offset_in_data as u64); + let ror_face_leaves = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_leaf_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_face_leaves = Vec::new(); + for vec in ror_face_leaves.iter() { + tree_face_leaves.push(vec.to_tree_face_leaf()); + } + + Vec::write_options(&tree_face_leaves, writer, options, ())?; + offset_in_data += 32 * object.object.tree_face_leaf_count as usize; + } else { + // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. + cursor.set_position(offset_in_data as u64); + let mn_face_leaves = Vec::::read_options( + &mut cursor, + &ReadOptions::new(binrw::Endian::Big), + binrw::VecArgs::builder() + .count(object.object.tree_face_leaf_count as usize) + .finalize(), + ) + .unwrap(); + + let mut tree_face_leaves = Vec::new(); + for vec in mn_face_leaves.iter() { + tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); + } + + Vec::write_options(&tree_face_leaves, writer, options, ())?; + offset_in_data += 20 * object.object.tree_face_leaf_count as usize; + } + assert_eq!(offset_in_data, args.streaming_data.len()); + } + } CollisionType::StreamingFinitePlane => { CollisionType::write_options(&CollisionType::FinitePlane, writer, options, ())?; diff --git a/src/lib.rs b/src/lib.rs index 722496e..1428fe3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub use crate::soi::*; pub use crate::soi_soup::*; pub use crate::str::*; pub use crate::toc::*; +pub use crate::utils::*; mod collision; mod model; diff --git a/src/soi.rs b/src/soi.rs index 5165200..6986b32 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -147,7 +147,7 @@ impl> Soi { pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { return &self.collision_models; } - + pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { let model_info = &texture.model_info; diff --git a/src/soi_soup.rs b/src/soi_soup.rs index 861b6e4..836ed45 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -49,7 +49,7 @@ impl> SoiSoup { pub fn static_textures(&self) -> &[StaticTexture] { self.soi.get_static_textures() } - + pub fn motion_packs(&self) -> &[StreamingMotionPack] { self.soi.get_motion_packs() } diff --git a/src/str.rs b/src/str.rs index de80777..8b7995c 100644 --- a/src/str.rs +++ b/src/str.rs @@ -6,7 +6,7 @@ use binrw::io; use flate2::read::ZlibDecoder; use crate::toc::ComponentKind; -use crate::{ComponentHeader, Section, MemoryEntry}; +use crate::{ComponentHeader, MemoryEntry, Section}; #[derive(Debug)] pub struct SectionData { @@ -43,20 +43,20 @@ impl Str { if let Some(zlib) = &header.zlib_header { let section_offset = header.memory_entry.offset as u64; self.file.seek(SeekFrom::Start(section_offset))?; - + let uncached = { - let data = self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; + let data = + self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; extract_components(§ion.uncached_components, data) }; - + let cached = { let data = self.decode_zlib_data(header.cached_data_size as usize, &zlib.cached_sizes)?; extract_components(§ion.cached_components, data) }; - + Ok(SectionData { uncached, cached }) - } - else { + } else { let section_offset = header.memory_entry.offset as u64; self.file.seek(SeekFrom::Start(section_offset))?; diff --git a/src/test.rs b/src/test.rs index 5bb3d7d..1a9d45b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,19 +4,16 @@ use std::path::{Path, PathBuf}; use binrw::{BinWrite, WriteOptions}; use x_flipper_360::*; -use crate::utils::*; +use crate::utils::*; use crate::ComponentKind::{self, *}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { - let toc_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.toc"); - let soi_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.soi"); - let str_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.str"); - + let toc_path = Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.toc"); + let soi_path = Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.soi"); + let str_path = Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.str"); + let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -33,23 +30,24 @@ fn extract() { } for static_texture in soup.static_textures().iter() { - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", clean_path(&static_texture.model_info.name))); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + clean_path(&static_texture.model_info.name) + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); out.write_all(&static_texture.header_file).unwrap(); } - } #[test] fn dump_scn() { let toc_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.toc"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.toc"); let soi_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.soi"); + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.soi"); let str_path = - Path::new("D:\\GigaLeak\\Rs_A_Mn\\RS_A.x360.str"); - + Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; @@ -59,26 +57,46 @@ fn dump_scn() { let section_data = str.read_section_data(section).unwrap(); for component in section_data.uncached { - print_component(&soup, id as u32, component, &mut num_anim_models, &mut num_static_models, &mut num_objects); + print_component( + &soup, + id as u32, + component, + &mut num_anim_models, + &mut num_static_models, + &mut num_objects, + ); } for component in section_data.cached { - print_component(&soup, id as u32, component, &mut num_anim_models, &mut num_static_models, &mut num_objects); + print_component( + &soup, + id as u32, + component, + &mut num_anim_models, + &mut num_static_models, + &mut num_objects, + ); } } } -fn print_component(soup: &SoiSoup, section_id: u32, component: ComponentData, num_anim_models: &mut i32, num_static_models: &mut i32, num_objects: &mut i32) { +fn print_component( + soup: &SoiSoup, + section_id: u32, + component: ComponentData, + num_anim_models: &mut i32, + num_static_models: &mut i32, + num_objects: &mut i32, +) { match component.kind { RenderableModel => { let header = soup .find_model(section_id, component.id, component.instance_id) .unwrap(); - if header.model_info.is_animated == 1{ + if header.model_info.is_animated == 1 { println!("[AnimatedModel{}]\n{}", num_anim_models, header); *num_anim_models = *num_anim_models + 1; - } - else { + } else { println!("[Model{}]\n{}", num_static_models, header); *num_static_models = *num_static_models + 1; } @@ -111,7 +129,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.got", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.got", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header.header.write(&mut out); @@ -123,7 +144,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.xng", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.xng", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -143,7 +167,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.gol", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.gol", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); let options = WriteOptions::new(binrw::Endian::Big); @@ -153,6 +180,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: &mut out, &options, CollisionModelArgs { + ror: false, streaming_data: component.data.clone(), }, ) @@ -181,7 +209,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -203,7 +234,35 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + component.path + )); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); + } + TextureFormat::Dxt2_3 => { + let texture_size: TextureSize2D = + TextureSize2D::from_bytes(metadata.texture_size().to_le_bytes()); + + let config = Config { + width: texture_size.width() as u32 + 1, + height: texture_size.height() as u32 + 1, + depth: None, + pitch: metadata.pitch() as u32, + tiled: metadata.tiled(), + packed_mips: metadata.packed_mips(), + format: Format::Dxt3, + mipmap_levels: Some(1.max(metadata.max_mip_level() - metadata.min_mip_level()) as u32), + base_address: metadata.base_address(), + mip_address: metadata.mip_address(), + }; + + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -225,7 +284,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -234,7 +296,10 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: } None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { - let path = PathBuf::from(format!("D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", component.path)); + let path = PathBuf::from(format!( + "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + component.path + )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); out.write_all(&static_texture.header_file).unwrap(); diff --git a/src/utils.rs b/src/utils.rs index c236136..d96c49d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,7 +15,7 @@ impl std::fmt::Display for Vector4 { } } -#[derive(Default, BinRead, BinWrite, Debug)] +#[derive(Default, BinRead, BinWrite, Debug, Clone, Copy)] #[brw(big)] pub struct Vector3 { pub x: f32, @@ -63,7 +63,7 @@ const NULL_BYTE: u8 = 0; const BACKSLASH: u8 = 92; const SLASH: u8 = 47; -pub(crate) fn clean_path(input: &[u8]) -> String { +pub fn clean_path(input: &[u8]) -> String { let mut output = Vec::new(); for c in input { @@ -81,7 +81,7 @@ pub(crate) fn clean_path(input: &[u8]) -> String { std::str::from_utf8(&output).unwrap().to_owned() } -pub(crate) fn clean_string(input: &[u8]) -> String { +pub fn clean_string(input: &[u8]) -> String { let mut output = Vec::new(); for c in input { From df0a998622be8ef33edf702cb098299dd5fa040c Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:24:36 -0500 Subject: [PATCH 09/23] print zone when dumping scene data --- src/collision.rs | 29 +++++++++++++++++++++-------- src/model.rs | 29 +++++++++++++++++++++-------- src/soi.rs | 2 +- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index fa54f3c..6a96a21 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -248,14 +248,27 @@ pub struct StreamingCollisionModel { impl std::fmt::Display for StreamingCollisionModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector - ); + if self.model_info.zone != -1 { + write!( + f, + "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + self.model_info.zone + ); + } + else { + write!( + f, + "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + ); + } Ok(for param in self.parameters.iter() { write!(f, "{}\n", param); }) diff --git a/src/model.rs b/src/model.rs index a65e0b0..69da109 100644 --- a/src/model.rs +++ b/src/model.rs @@ -125,14 +125,27 @@ pub struct StreamingRenderableModel { impl std::fmt::Display for StreamingRenderableModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector - ); + if self.model_info.zone != -1 { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + self.model_info.zone + ); + } + else { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + ); + } Ok(for param in self.parameters.iter() { write!(f, "{}\n", param); }) diff --git a/src/soi.rs b/src/soi.rs index 6986b32..8f245a5 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -53,7 +53,7 @@ pub struct ModelInfo { pub name: [u8; 260], - zone: i32, + pub zone: i32, pub parameter_count: i32, } From ee29056cec82337ddb2d76a82cd377d1a1d35d6e Mon Sep 17 00:00:00 2001 From: Marcel <34819524+MarcelCoding@users.noreply.github.com> Date: Mon, 13 Feb 2023 22:19:21 +0100 Subject: [PATCH 10/23] WIP --- src/collision.rs | 171 ++++++++++++++++++++++++----------------------- src/model.rs | 73 ++++++++++---------- src/motion.rs | 6 +- src/soi.rs | 4 +- src/str.rs | 2 +- src/toc.rs | 2 +- src/utils.rs | 1 + 7 files changed, 132 insertions(+), 127 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index fa54f3c..6f6f91f 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -1,5 +1,7 @@ +use binrw::{BinRead, BinResult, BinWrite, Endian}; +use std::io::{Seek, Write}; + use crate::utils::*; -use binrw::{BinRead, BinResult, BinWrite, ReadOptions}; // Members that are commented out are part of the streaming data section, and need to be merged into the contents of the header data after extraction. @@ -218,19 +220,19 @@ pub struct CollisionModel { version: i32, collision_type: CollisionType, - #[br(if(collision_type == CollisionType::StreamingSoultree))] + #[br(if (collision_type == CollisionType::StreamingSoultree))] object: SoultreeCollisionObject, - #[br(if(collision_type == CollisionType::StreamingHeirarchy))] + #[br(if (collision_type == CollisionType::StreamingHeirarchy))] object_count: i32, - #[br(if(collision_type == CollisionType::StreamingHeirarchy))] + #[br(if (collision_type == CollisionType::StreamingHeirarchy))] reverse_collision_mode: i32, - #[br(if(collision_type == CollisionType::StreamingHeirarchy), count = object_count)] + #[br(if (collision_type == CollisionType::StreamingHeirarchy), count = object_count)] objects: Vec, - #[br(if(collision_type == CollisionType::StreamingFinitePlane))] + #[br(if (collision_type == CollisionType::StreamingFinitePlane))] plane_count: i32, - #[br(if(collision_type == CollisionType::StreamingFinitePlane))] + #[br(if (collision_type == CollisionType::StreamingFinitePlane))] half: Vector3, // #[br(if(collision_type == CollisionType::FinitePlane), count = plane_count)] // planes: Vec, @@ -262,7 +264,8 @@ impl std::fmt::Display for StreamingCollisionModel { } } -#[derive(binrw::BinrwNamedArgs, Clone, Debug)] +// Derive BinrwNamedArgs +#[derive(Clone, Debug)] pub struct CollisionModelArgs { pub ror: bool, pub streaming_data: Vec, @@ -271,18 +274,18 @@ pub struct CollisionModelArgs { // This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper GOL file. // As such, the streaming data must be passed to write_options. impl BinWrite for CollisionModel { - type Args = CollisionModelArgs; + type Args<'a> = &'a CollisionModelArgs; - fn write_options( + fn write_options( &self, writer: &mut W, - options: &binrw::WriteOptions, - args: Self::Args, - ) -> binrw::BinResult<()> { + endian: Endian, + args: Self::Args<'_>, + ) -> BinResult<()> { let magic = b"\x00\x00\x04\xD2".to_vec(); - Vec::write_options(&magic, writer, options, ())?; - self.col_type.write_options(writer, options, ())?; - i32::write_options(&self.version, writer, options, ())?; + Vec::write_options(&magic, writer, endian, ())?; + self.col_type.write_options(writer, endian, ())?; + i32::write_options(&self.version, writer, endian, ())?; let mut offset_in_data: usize = 0; @@ -299,15 +302,15 @@ impl BinWrite for CollisionModel { CollisionType::Ecosystem => panic!("Unsupported collision type!"), CollisionType::FinitePlane => panic!("Unsupported collision type!"), CollisionType::StreamingSoultree => { - CollisionType::write_options(&CollisionType::Soultree, writer, options, ())?; + CollisionType::write_options(&CollisionType::Soultree, writer, endian, ())?; - i32::write_options(&self.object.temp_cmt, writer, options, ())?; - self.object.obb_data.write_options(writer, options, ())?; - i32::write_options(&self.object.reverse_collision_mode, writer, options, ())?; - u32::write_options(&self.object.vertex_count, writer, options, ())?; - u32::write_options(&self.object.tree_face_count, writer, options, ())?; - u32::write_options(&self.object.tree_face_leaf_count, writer, options, ())?; - i32::write_options(&self.object.quantized, writer, options, ())?; + i32::write_options(&self.object.temp_cmt, writer, endian, ())?; + self.object.obb_data.write_options(writer, endian, ())?; + i32::write_options(&self.object.reverse_collision_mode, writer, endian, ())?; + u32::write_options(&self.object.vertex_count, writer, endian, ())?; + u32::write_options(&self.object.tree_face_count, writer, endian, ())?; + u32::write_options(&self.object.tree_face_leaf_count, writer, endian, ())?; + i32::write_options(&self.object.quantized, writer, endian, ())?; // Conversion from MN's streaming format requires the vertices be cached and used later for DValue calculation. let mut global_vertices = Vec::new(); @@ -318,7 +321,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -334,20 +337,20 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, options, ())?; + Vec::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 16 * self.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); global_vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - Vec::write_options(&global_vertices, writer, options, ())?; + Vec::write_options(&global_vertices, writer, endian, ())?; offset_in_data += 12 * self.object.vertex_count as usize; // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]).to_vec(); @@ -361,7 +364,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -377,20 +380,20 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, options, ())?; + Vec::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 8 * self.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); let quantized_vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), ) .unwrap(); - Vec::write_options(&quantized_vertices, writer, options, ())?; + Vec::write_options(&quantized_vertices, writer, endian, ())?; offset_in_data += 6 * self.object.vertex_count as usize; for vec in quantized_vertices.iter() { @@ -406,7 +409,7 @@ impl BinWrite for CollisionModel { // offset_in_data += 6 * self.object.vertex_count as usize; } } - i32::write_options(&self.object.load_normals, writer, options, ())?; + i32::write_options(&self.object.load_normals, writer, endian, ())?; if self.object.load_normals == 1 { if self.object.quantized == 0 { if args.ror { @@ -414,7 +417,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -430,13 +433,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, options, ())?; + Vec::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 16 * self.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, options, ())?; + Vec::write_options(&data, writer, endian, ())?; offset_in_data += 12 * self.object.vertex_count as usize; } } @@ -446,7 +449,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -462,13 +465,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, options, ())?; + Vec::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 8 * self.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 6 * self.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, options, ())?; + Vec::write_options(&data, writer, endian, ())?; offset_in_data += 6 * self.object.vertex_count as usize; } } @@ -482,7 +485,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_tree_faces = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.tree_face_count as usize) .finalize(), @@ -494,14 +497,14 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, options, ())?; + Vec::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 64 * self.object.tree_face_count as usize; } else { // Read StreamingDataTreeFaces from the data and write out TreeFaces. cursor.set_position(offset_in_data as u64); let mn_tree_faces = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.tree_face_count as usize) .finalize(), @@ -513,7 +516,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, options, ())?; + Vec::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 36 * self.object.tree_face_count as usize; } if args.ror { @@ -521,7 +524,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_face_leaves = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.tree_face_leaf_count as usize) .finalize(), @@ -533,14 +536,14 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf()); } - Vec::write_options(&tree_face_leaves, writer, options, ())?; + Vec::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 32 * self.object.tree_face_leaf_count as usize; } else { // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. cursor.set_position(offset_in_data as u64); let mn_face_leaves = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(self.object.tree_face_leaf_count as usize) .finalize(), @@ -552,24 +555,24 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); } - Vec::write_options(&tree_face_leaves, writer, options, ())?; + Vec::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 20 * self.object.tree_face_leaf_count as usize; } assert_eq!(offset_in_data, args.streaming_data.len()); } CollisionType::StreamingHeirarchy => { - CollisionType::write_options(&CollisionType::SoultreeHeirarchy, writer, options, ())?; + CollisionType::write_options(&CollisionType::SoultreeHeirarchy, writer, endian, ())?; - i32::write_options(&self.object_count, writer, options, ())?; - i32::write_options(&self.reverse_collision_mode, writer, options, ())?; + i32::write_options(&self.object_count, writer, endian, ())?; + i32::write_options(&self.reverse_collision_mode, writer, endian, ())?; for object in &self.objects { - i32::write_options(&object.object.temp_cmt, writer, options, ())?; - object.object.obb_data.write_options(writer, options, ())?; - i32::write_options(&object.object.reverse_collision_mode, writer, options, ())?; - u32::write_options(&object.object.vertex_count, writer, options, ())?; - u32::write_options(&object.object.tree_face_count, writer, options, ())?; - u32::write_options(&object.object.tree_face_leaf_count, writer, options, ())?; - i32::write_options(&object.object.quantized, writer, options, ())?; + i32::write_options(&object.object.temp_cmt, writer, endian, ())?; + object.object.obb_data.write_options(writer, endian, ())?; + i32::write_options(&object.object.reverse_collision_mode, writer, endian, ())?; + u32::write_options(&object.object.vertex_count, writer, endian, ())?; + u32::write_options(&object.object.tree_face_count, writer, endian, ())?; + u32::write_options(&object.object.tree_face_leaf_count, writer, endian, ())?; + i32::write_options(&object.object.quantized, writer, endian, ())?; // Conversion from MN's streaming format requires the vertices be cached and used later for DValue calculation. let mut global_vertices = Vec::new(); @@ -580,7 +583,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -596,20 +599,20 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, options, ())?; + Vec::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 16 * object.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); global_vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), ) .unwrap(); - Vec::write_options(&global_vertices, writer, options, ())?; + Vec::write_options(&global_vertices, writer, endian, ())?; offset_in_data += 12 * object.object.vertex_count as usize; // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]).to_vec(); @@ -623,7 +626,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -639,20 +642,20 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, options, ())?; + Vec::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 8 * object.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); let quantized_vertices = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), ) .unwrap(); - Vec::write_options(&quantized_vertices, writer, options, ())?; + Vec::write_options(&quantized_vertices, writer, endian, ())?; offset_in_data += 6 * object.object.vertex_count as usize; for vec in quantized_vertices.iter() { @@ -668,7 +671,7 @@ impl BinWrite for CollisionModel { // offset_in_data += 6 * object.object.vertex_count as usize; } } - i32::write_options(&object.object.load_normals, writer, options, ())?; + i32::write_options(&object.object.load_normals, writer, endian, ())?; if object.object.load_normals == 1 { if object.object.quantized == 0 { if args.ror { @@ -676,7 +679,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -692,13 +695,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, options, ())?; + Vec::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 16 * object.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, options, ())?; + Vec::write_options(&data, writer, endian, ())?; offset_in_data += 12 * object.object.vertex_count as usize; } } @@ -708,7 +711,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -724,13 +727,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, options, ())?; + Vec::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 8 * object.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 6 * object.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, options, ())?; + Vec::write_options(&data, writer, endian, ())?; offset_in_data += 6 * object.object.vertex_count as usize; } } @@ -744,7 +747,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_tree_faces = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.tree_face_count as usize) .finalize(), @@ -756,14 +759,14 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, options, ())?; + Vec::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 64 * object.object.tree_face_count as usize; } else { // Read StreamingDataTreeFaces from the data and write out TreeFaces. cursor.set_position(offset_in_data as u64); let mn_tree_faces = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.tree_face_count as usize) .finalize(), @@ -775,7 +778,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, options, ())?; + Vec::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 36 * object.object.tree_face_count as usize; } if args.ror { @@ -783,7 +786,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_face_leaves = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.tree_face_leaf_count as usize) .finalize(), @@ -795,14 +798,14 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf()); } - Vec::write_options(&tree_face_leaves, writer, options, ())?; + Vec::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 32 * object.object.tree_face_leaf_count as usize; } else { // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. cursor.set_position(offset_in_data as u64); let mn_face_leaves = Vec::::read_options( &mut cursor, - &ReadOptions::new(binrw::Endian::Big), + binrw::Endian::Big, binrw::VecArgs::builder() .count(object.object.tree_face_leaf_count as usize) .finalize(), @@ -814,19 +817,19 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); } - Vec::write_options(&tree_face_leaves, writer, options, ())?; + Vec::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 20 * object.object.tree_face_leaf_count as usize; } assert_eq!(offset_in_data, args.streaming_data.len()); } } CollisionType::StreamingFinitePlane => { - CollisionType::write_options(&CollisionType::FinitePlane, writer, options, ())?; + CollisionType::write_options(&CollisionType::FinitePlane, writer, endian, ())?; - i32::write_options(&self.plane_count, writer, options, ())?; - Vector3::write_options(&self.half, writer, options, ())?; + i32::write_options(&self.plane_count, writer, endian, ())?; + Vector3::write_options(&self.half, writer, endian, ())?; - Vec::write_options(&args.streaming_data, writer, options, ())?; + Vec::write_options(&args.streaming_data, writer, endian, ())?; // assert_eq!(self.plane_count as usize * 60, args.streaming_data.len()); } } diff --git a/src/model.rs b/src/model.rs index a65e0b0..a145ac2 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1,7 +1,7 @@ -use std::str::Bytes; +use binrw::{BinRead, BinResult, BinWrite, Endian}; +use std::io::{Seek, Write}; use crate::utils::*; -use binrw::{BinRead, BinResult, BinWrite, BinrwNamedArgs}; #[derive(BinRead, BinWrite, Debug)] #[brw(big)] @@ -59,7 +59,7 @@ pub struct StreamingXNGMesh { pub surface_index: u32, pub vertex_type: u32, - #[br(if((vertex_type & 0x2000) == 0x2000))] + #[br(if ((vertex_type & 0x2000) == 0x2000))] pub compression_stuff: Option<[f32; 8]>, pub num_texture_coordinate_sets: u8, @@ -75,7 +75,7 @@ pub struct StreamingXNGMesh { pub num_vertices: u16, pub num_face_indices: u16, - #[br(if((vertex_type & 0x100) == 0x100))] + #[br(if ((vertex_type & 0x100) == 0x100))] pub delta_block: Option, } @@ -139,7 +139,8 @@ impl std::fmt::Display for StreamingRenderableModel { } } -#[derive(BinrwNamedArgs, Clone, Debug)] +// BinrwNamedArgs +#[derive(Clone, Debug)] pub struct XNGHeaderArgs { pub streaming_data: Vec, } @@ -147,47 +148,47 @@ pub struct XNGHeaderArgs { // This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper XNG file. // As such, the streaming data must be passed to write_options. impl BinWrite for XNGHeader { - type Args = XNGHeaderArgs; + type Args<'a> = &'a XNGHeaderArgs; - fn write_options( + fn write_options( &self, writer: &mut W, - options: &binrw::WriteOptions, - args: Self::Args, - ) -> binrw::BinResult<()> { + endian: Endian, + args: Self::Args<'_>, + ) -> BinResult<()> { let magic = b"xng\0".to_vec(); - Vec::write_options(&magic, writer, options, ())?; - i32::write_options(&self.version, writer, options, ())?; - u32::write_options(&self.num_bones, writer, options, ())?; - Vec::write_options(&self.bones, writer, options, ())?; - i32::write_options(&self.num_mesh_names, writer, options, ())?; - Vec::write_options(&self.mesh_names, writer, options, ())?; - u8::write_options(&self.num_lod, writer, options, ())?; - u8::write_options(&self.skin_animates_flag, writer, options, ())?; - u8::write_options(&self.has_weight, writer, options, ())?; - u8::write_options(&self.unused, writer, options, ())?; + Vec::write_options(&magic, writer, endian, ())?; + i32::write_options(&self.version, writer, endian, ())?; + u32::write_options(&self.num_bones, writer, endian, ())?; + Vec::write_options(&self.bones, writer, endian, ())?; + i32::write_options(&self.num_mesh_names, writer, endian, ())?; + Vec::write_options(&self.mesh_names, writer, endian, ())?; + u8::write_options(&self.num_lod, writer, endian, ())?; + u8::write_options(&self.skin_animates_flag, writer, endian, ())?; + u8::write_options(&self.has_weight, writer, endian, ())?; + u8::write_options(&self.unused, writer, endian, ())?; let mut offset_in_data: usize = 0; for lod in &self.lods { - f32::write_options(&lod.auto_lod_value, writer, options, ())?; - u32::write_options(&lod.num_meshes, writer, options, ())?; + f32::write_options(&lod.auto_lod_value, writer, endian, ())?; + u32::write_options(&lod.num_meshes, writer, endian, ())?; for mesh in &lod.meshes { - u32::write_options(&mesh.surface_index, writer, options, ())?; - u32::write_options(&mesh.vertex_type, writer, options, ())?; + u32::write_options(&mesh.surface_index, writer, endian, ())?; + u32::write_options(&mesh.vertex_type, writer, endian, ())?; if let Some(compression_stuff) = mesh.compression_stuff { - compression_stuff.write_options(writer, options, ())?; + compression_stuff.write_options(writer, endian, ())?; } - u8::write_options(&mesh.num_texture_coordinate_sets, writer, options, ())?; - u8::write_options(&mesh.compressed, writer, options, ())?; - u8::write_options(&0, writer, options, ())?; - u8::write_options(&mesh.unk, writer, options, ())?; - u8::write_options(&mesh.unk2, writer, options, ())?; - u8::write_options(&mesh.unk3, writer, options, ())?; - Vec::write_options(&mesh.texture_coordinate_sets, writer, options, ())?; + u8::write_options(&mesh.num_texture_coordinate_sets, writer, endian, ())?; + u8::write_options(&mesh.compressed, writer, endian, ())?; + u8::write_options(&0, writer, endian, ())?; + u8::write_options(&mesh.unk, writer, endian, ())?; + u8::write_options(&mesh.unk2, writer, endian, ())?; + u8::write_options(&mesh.unk3, writer, endian, ())?; + Vec::write_options(&mesh.texture_coordinate_sets, writer, endian, ())?; - u16::write_options(&mesh.num_vertices, writer, options, ())?; - u16::write_options(&mesh.num_face_indices, writer, options, ())?; + u16::write_options(&mesh.num_vertices, writer, endian, ())?; + u16::write_options(&mesh.num_face_indices, writer, endian, ())?; assert!(mesh.streaming == 1); @@ -228,12 +229,12 @@ impl BinWrite for XNGHeader { } let data = (&args.streaming_data[offset_in_data..offset_in_data + offset]).to_vec(); - Vec::write_options(&data, writer, options, ())?; + Vec::write_options(&data, writer, endian, ())?; offset_in_data += offset; if let Some(delta_block) = &mesh.delta_block { - delta_block.write_options(writer, options, ())?; + delta_block.write_options(writer, endian, ())?; } } } diff --git a/src/motion.rs b/src/motion.rs index 2eb1019..5a2b343 100644 --- a/src/motion.rs +++ b/src/motion.rs @@ -1,4 +1,4 @@ -use binrw::{BinRead, BinResult, BinWrite}; +use binrw::{BinRead, BinWrite}; #[derive(BinRead, BinWrite, Debug)] #[br(big)] @@ -30,11 +30,11 @@ pub struct StreamingMotionPackHeader { num_camera_infos: u32, padding: u32, - #[br(if(version == -8))] + #[br(if (version == - 8))] #[bw(ignore)] unk_bpb_size: u32, - #[br(if(version == -8), count = unk_bpb_size)] + #[br(if (version == - 8), count = unk_bpb_size)] #[bw(ignore)] unk_bpb: Vec, } diff --git a/src/soi.rs b/src/soi.rs index 0f4ad94..4249e26 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -1,7 +1,7 @@ use std::fs::File; use std::path::Path; -use binrw::{io::SeekFrom, BinRead, BinReaderExt, BinResult, BinWrite}; +use binrw::{BinRead, BinReaderExt, BinResult}; use crate::collision::*; use crate::model::*; @@ -75,7 +75,7 @@ impl std::fmt::Display for StreamingParameter { } #[derive(BinRead, Debug)] -pub struct StreamingTexture = ()> + 'static>{ +pub struct StreamingTexture = ()> + 'static> { pub model_info: ModelInfo, pub padding: u32, pub header: TH, diff --git a/src/str.rs b/src/str.rs index 8b7995c..328cf10 100644 --- a/src/str.rs +++ b/src/str.rs @@ -6,7 +6,7 @@ use binrw::io; use flate2::read::ZlibDecoder; use crate::toc::ComponentKind; -use crate::{ComponentHeader, MemoryEntry, Section}; +use crate::{ComponentHeader, Section}; #[derive(Debug)] pub struct SectionData { diff --git a/src/toc.rs b/src/toc.rs index ed22e87..7b84001 100644 --- a/src/toc.rs +++ b/src/toc.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::Seek; use std::path::Path; -use binrw::{BinRead, BinReaderExt, BinResult}; +use binrw::{BinRead, BinResult}; use crate::utils::clean_path; diff --git a/src/utils.rs b/src/utils.rs index d462666..d9d50b4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,6 +8,7 @@ pub struct Vector4 { pub z: f32, pub w: f32, } + const NULL_BYTE: u8 = b'\0'; const BACKSLASH: u8 = b'\\'; const SLASH: char = '/'; From 8ac8b41e31f260e88c5860ac6bfaa76cd5fa4b23 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Tue, 13 Jun 2023 14:22:05 -0400 Subject: [PATCH 11/23] update binrw again --- .vscode/launch.json | 27 ++++++++++++++++++++ Cargo.toml | 2 +- src/collision.rs | 60 ++++++++++++++++++++++----------------------- src/model.rs | 10 ++++---- src/test.rs | 38 ++++++++++++++-------------- 5 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3a69f38 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'soiboy'", + "cargo": { + "args": [ + "test", + "--no-run", + // "--lib", + "--package=soiboy" + ], + "filter": { + "name": "soiboy", + // "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index dd6c220..27d38cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] modular-bitfield = "0.11" flate2 = "1.0" -binrw = "0.11" +binrw = "0.11.2" [dev-dependencies] x-flipper-360 = { git = "https://github.com/offsetting/x-flipper-360" } diff --git a/src/collision.rs b/src/collision.rs index b14149b..b50e74c 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -296,7 +296,7 @@ impl BinWrite for CollisionModel { args: Self::Args<'_>, ) -> BinResult<()> { let magic = b"\x00\x00\x04\xD2".to_vec(); - Vec::write_options(&magic, writer, endian, ())?; + Vec::::write_options(&magic, writer, endian, ())?; self.col_type.write_options(writer, endian, ())?; i32::write_options(&self.version, writer, endian, ())?; @@ -350,7 +350,7 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, endian, ())?; + Vec::::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 16 * self.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); @@ -363,11 +363,11 @@ impl BinWrite for CollisionModel { ) .unwrap(); - Vec::write_options(&global_vertices, writer, endian, ())?; + Vec::::write_options(&global_vertices, writer, endian, ())?; offset_in_data += 12 * self.object.vertex_count as usize; // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]).to_vec(); - // Vec::write_options(&data, writer, options, ())?; + // Vec::::write_options(&data, writer, options, ())?; // offset_in_data += 12 * self.object.vertex_count as usize; } } @@ -393,7 +393,7 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, endian, ())?; + Vec::::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 8 * self.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); @@ -406,7 +406,7 @@ impl BinWrite for CollisionModel { ) .unwrap(); - Vec::write_options(&quantized_vertices, writer, endian, ())?; + Vec::::write_options(&quantized_vertices, writer, endian, ())?; offset_in_data += 6 * self.object.vertex_count as usize; for vec in quantized_vertices.iter() { @@ -418,7 +418,7 @@ impl BinWrite for CollisionModel { } // let data = (&args.streaming_data[offset_in_data..offset_in_data + 6 * self.object.vertex_count as usize]).to_vec(); - // Vec::write_options(&data, writer, options, ())?; + // Vec::::write_options(&data, writer, options, ())?; // offset_in_data += 6 * self.object.vertex_count as usize; } } @@ -446,13 +446,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, endian, ())?; + Vec::::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 16 * self.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 12 * self.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, endian, ())?; + Vec::::write_options(&data, writer, endian, ())?; offset_in_data += 12 * self.object.vertex_count as usize; } } @@ -478,13 +478,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, endian, ())?; + Vec::::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 8 * self.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 6 * self.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, endian, ())?; + Vec::::write_options(&data, writer, endian, ())?; offset_in_data += 6 * self.object.vertex_count as usize; } } @@ -510,7 +510,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, endian, ())?; + Vec::::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 64 * self.object.tree_face_count as usize; } else { // Read StreamingDataTreeFaces from the data and write out TreeFaces. @@ -529,7 +529,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, endian, ())?; + Vec::::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 36 * self.object.tree_face_count as usize; } if args.ror { @@ -549,7 +549,7 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf()); } - Vec::write_options(&tree_face_leaves, writer, endian, ())?; + Vec::::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 32 * self.object.tree_face_leaf_count as usize; } else { // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. @@ -568,7 +568,7 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); } - Vec::write_options(&tree_face_leaves, writer, endian, ())?; + Vec::::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 20 * self.object.tree_face_leaf_count as usize; } assert_eq!(offset_in_data, args.streaming_data.len()); @@ -612,7 +612,7 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, endian, ())?; + Vec::::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 16 * object.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); @@ -625,11 +625,11 @@ impl BinWrite for CollisionModel { ) .unwrap(); - Vec::write_options(&global_vertices, writer, endian, ())?; + Vec::::write_options(&global_vertices, writer, endian, ())?; offset_in_data += 12 * object.object.vertex_count as usize; // let data = (&args.streaming_data[offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]).to_vec(); - // Vec::write_options(&data, writer, options, ())?; + // Vec::::write_options(&data, writer, options, ())?; // offset_in_data += 12 * object.object.vertex_count as usize; } } @@ -655,7 +655,7 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_vertices, writer, endian, ())?; + Vec::::write_options(&truncated_vertices, writer, endian, ())?; offset_in_data += 8 * object.object.vertex_count as usize; } else { cursor.set_position(offset_in_data as u64); @@ -668,7 +668,7 @@ impl BinWrite for CollisionModel { ) .unwrap(); - Vec::write_options(&quantized_vertices, writer, endian, ())?; + Vec::::write_options(&quantized_vertices, writer, endian, ())?; offset_in_data += 6 * object.object.vertex_count as usize; for vec in quantized_vertices.iter() { @@ -680,7 +680,7 @@ impl BinWrite for CollisionModel { } // let data = (&args.streaming_data[offset_in_data..offset_in_data + 6 * object.object.vertex_count as usize]).to_vec(); - // Vec::write_options(&data, writer, options, ())?; + // Vec::::write_options(&data, writer, options, ())?; // offset_in_data += 6 * object.object.vertex_count as usize; } } @@ -708,13 +708,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, endian, ())?; + Vec::::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 16 * object.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 12 * object.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, endian, ())?; + Vec::::write_options(&data, writer, endian, ())?; offset_in_data += 12 * object.object.vertex_count as usize; } } @@ -740,13 +740,13 @@ impl BinWrite for CollisionModel { }); } - Vec::write_options(&truncated_normals, writer, endian, ())?; + Vec::::write_options(&truncated_normals, writer, endian, ())?; offset_in_data += 8 * object.object.vertex_count as usize; } else { let data = (&args.streaming_data [offset_in_data..offset_in_data + 6 * object.object.vertex_count as usize]) .to_vec(); - Vec::write_options(&data, writer, endian, ())?; + Vec::::write_options(&data, writer, endian, ())?; offset_in_data += 6 * object.object.vertex_count as usize; } } @@ -772,7 +772,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, endian, ())?; + Vec::::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 64 * object.object.tree_face_count as usize; } else { // Read StreamingDataTreeFaces from the data and write out TreeFaces. @@ -791,7 +791,7 @@ impl BinWrite for CollisionModel { tree_faces.push(vec.to_tree_face()); } - Vec::write_options(&tree_faces, writer, endian, ())?; + Vec::::write_options(&tree_faces, writer, endian, ())?; offset_in_data += 36 * object.object.tree_face_count as usize; } if args.ror { @@ -811,7 +811,7 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf()); } - Vec::write_options(&tree_face_leaves, writer, endian, ())?; + Vec::::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 32 * object.object.tree_face_leaf_count as usize; } else { // Read StreamingDataTreeFaceLeaves from the data and write out TreeFaceLeaves. @@ -830,7 +830,7 @@ impl BinWrite for CollisionModel { tree_face_leaves.push(vec.to_tree_face_leaf(&global_vertices)); } - Vec::write_options(&tree_face_leaves, writer, endian, ())?; + Vec::::write_options(&tree_face_leaves, writer, endian, ())?; offset_in_data += 20 * object.object.tree_face_leaf_count as usize; } assert_eq!(offset_in_data, args.streaming_data.len()); @@ -842,7 +842,7 @@ impl BinWrite for CollisionModel { i32::write_options(&self.plane_count, writer, endian, ())?; Vector3::write_options(&self.half, writer, endian, ())?; - Vec::write_options(&args.streaming_data, writer, endian, ())?; + Vec::::write_options(&args.streaming_data, writer, endian, ())?; // assert_eq!(self.plane_count as usize * 60, args.streaming_data.len()); } } diff --git a/src/model.rs b/src/model.rs index 1b46a1e..8bb4101 100644 --- a/src/model.rs +++ b/src/model.rs @@ -170,12 +170,12 @@ impl BinWrite for XNGHeader { args: Self::Args<'_>, ) -> BinResult<()> { let magic = b"xng\0".to_vec(); - Vec::write_options(&magic, writer, endian, ())?; + Vec::::write_options(&magic, writer, endian, ())?; i32::write_options(&self.version, writer, endian, ())?; u32::write_options(&self.num_bones, writer, endian, ())?; - Vec::write_options(&self.bones, writer, endian, ())?; + Vec::::write_options(&self.bones, writer, endian, ())?; i32::write_options(&self.num_mesh_names, writer, endian, ())?; - Vec::write_options(&self.mesh_names, writer, endian, ())?; + Vec::::write_options(&self.mesh_names, writer, endian, ())?; u8::write_options(&self.num_lod, writer, endian, ())?; u8::write_options(&self.skin_animates_flag, writer, endian, ())?; u8::write_options(&self.has_weight, writer, endian, ())?; @@ -198,7 +198,7 @@ impl BinWrite for XNGHeader { u8::write_options(&mesh.unk, writer, endian, ())?; u8::write_options(&mesh.unk2, writer, endian, ())?; u8::write_options(&mesh.unk3, writer, endian, ())?; - Vec::write_options(&mesh.texture_coordinate_sets, writer, endian, ())?; + Vec::::write_options(&mesh.texture_coordinate_sets, writer, endian, ())?; u16::write_options(&mesh.num_vertices, writer, endian, ())?; u16::write_options(&mesh.num_face_indices, writer, endian, ())?; @@ -242,7 +242,7 @@ impl BinWrite for XNGHeader { } let data = (&args.streaming_data[offset_in_data..offset_in_data + offset]).to_vec(); - Vec::write_options(&data, writer, endian, ())?; + Vec::::write_options(&data, writer, endian, ())?; offset_in_data += offset; diff --git a/src/test.rs b/src/test.rs index e0784f2..f346e8a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,7 +1,7 @@ use std::io::Write; use std::path::{Path, PathBuf}; -use binrw::{BinWrite, WriteOptions}; +use binrw::{BinWrite}; use x_flipper_360::*; use crate::utils::*; @@ -12,7 +12,7 @@ use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; fn extract() { let toc_path = Path::new("./data/VehicleInfo.x360.toc"); let soi_path = Path::new("./data/VehicleInfo.x360.soi"); - let str_path = Path::new("data/VehicleInfo.x360.str"); + let str_path = Path::new("./data/VehicleInfo.x360.str"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); @@ -31,7 +31,7 @@ fn extract() { for static_texture in soup.static_textures().iter() { let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", clean_path(&static_texture.model_info.name) )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -43,11 +43,11 @@ fn extract() { #[test] fn dump_scn() { let toc_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.toc"); + Path::new("./data/VehicleInfo.x360.toc"); let soi_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.soi"); + Path::new("./data/VehicleInfo.x360.soi"); let str_path = - Path::new("D:\\Xbox 360\\RoRX360_Extracted\\RELEASE (NEW)\\C\\Scenes\\CR_04\\CR_04.x360.str"); + Path::new("./data/VehicleInfo.x360.res"); let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; @@ -130,7 +130,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .unwrap(); let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.got", + ".\\data\\VehicleInfo\\{}.got", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -145,18 +145,17 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .unwrap(); let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.xng", + ".\\data\\VehicleInfo\\{}.xng", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - let options = WriteOptions::new(binrw::Endian::Big); header .streaming_model_header .write_options( &mut out, - &options, - XNGHeaderArgs { + binrw::Endian::Big, + &XNGHeaderArgs { streaming_data: component.data.clone(), }, ) @@ -168,18 +167,17 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: .unwrap(); let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.gol", + ".\\data\\VehicleInfo\\{}.gol", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - let options = WriteOptions::new(binrw::Endian::Big); header .collision_model .write_options( &mut out, - &options, - CollisionModelArgs { + binrw::Endian::Big, + &CollisionModelArgs { ror: false, streaming_data: component.data.clone(), }, @@ -210,7 +208,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: }; let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -235,7 +233,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: }; let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -260,7 +258,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: }; let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -285,7 +283,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: }; let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -297,7 +295,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { let path = PathBuf::from(format!( - "D:\\GigaLeak\\Rs_A_Mn\\Out\\{}.dds", + ".\\data\\VehicleInfo\\{}.dds", component.path )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); From 992badc9d583ce8347c96cdf40350a37fd644a0d Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 14 Jun 2023 19:15:58 -0400 Subject: [PATCH 12/23] add wii texture stuff --- src/collision.rs | 41 +++-- src/lib.rs | 5 +- src/models/mod.rs | 2 + src/{model.rs => models/xng.rs} | 41 +++-- src/soi.rs | 91 +++++----- src/soi_soup.rs | 68 +++---- src/str.rs | 36 ++-- src/test.rs | 306 +++++++++++++++++++++----------- src/wii_th.rs | 107 +++++++++++ 9 files changed, 458 insertions(+), 239 deletions(-) create mode 100644 src/models/mod.rs rename src/{model.rs => models/xng.rs} (91%) create mode 100644 src/wii_th.rs diff --git a/src/collision.rs b/src/collision.rs index b50e74c..307a903 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -250,27 +250,26 @@ pub struct StreamingCollisionModel { impl std::fmt::Display for StreamingCollisionModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.model_info.zone != -1 { - write!( - f, - "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - self.model_info.zone - ); - } - else { - write!( - f, - "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - ); - } + if self.model_info.zone != -1 { + write!( + f, + "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + self.model_info.zone + ); + } else { + write!( + f, + "COL={}.col\nPosition={}\nLookVector={}\nUpVector={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + ); + } Ok(for param in self.parameters.iter() { write!(f, "{}\n", param); }) diff --git a/src/lib.rs b/src/lib.rs index 1428fe3..5071366 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub use crate::collision::*; -pub use crate::model::*; +pub use crate::models::*; pub use crate::motion::*; pub use crate::soi::*; pub use crate::soi_soup::*; @@ -8,13 +8,14 @@ pub use crate::toc::*; pub use crate::utils::*; mod collision; -mod model; +mod models; mod motion; mod soi; mod soi_soup; mod str; mod toc; mod utils; +mod wii_th; #[cfg(test)] mod test; diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..6acf3f0 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,2 @@ +mod xng; +pub use self::xng::*; \ No newline at end of file diff --git a/src/model.rs b/src/models/xng.rs similarity index 91% rename from src/model.rs rename to src/models/xng.rs index 8bb4101..e32a10d 100644 --- a/src/model.rs +++ b/src/models/xng.rs @@ -125,27 +125,26 @@ pub struct StreamingRenderableModel { impl std::fmt::Display for StreamingRenderableModel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.model_info.zone != -1 { - write!( - f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - self.model_info.zone - ); - } - else { - write!( - f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - ); - } + if self.model_info.zone != -1 { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + self.model_info.zone + ); + } else { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + ); + } Ok(for param in self.parameters.iter() { write!(f, "{}\n", param); }) diff --git a/src/soi.rs b/src/soi.rs index 1fdc9df..67cf02f 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -4,7 +4,7 @@ use std::path::Path; use binrw::{BinRead, BinReaderExt, BinResult}; use crate::collision::*; -use crate::model::*; +use crate::models::*; use crate::motion::*; use crate::utils::*; @@ -77,7 +77,7 @@ impl std::fmt::Display for StreamingParameter { #[derive(BinRead, Debug)] pub struct StreamingTexture = ()> + 'static> { pub model_info: ModelInfo, - pub padding: u32, + //pub padding: u32, pub header: TH, } @@ -108,14 +108,13 @@ pub struct Soi = ()> + 'static> { #[br(count = header.motion_packs)] motion_packs: Vec, - // #[br(if(header.flags & 64 == 1))] // collision_grid_info: StreamingCollisionGridInfo, - #[br(count = header.renderable_models)] - renderable_models: Vec, + // #[br(count = header.renderable_models)] + // renderable_models: Vec, - #[br(count = header.collision_models)] - collision_models: Vec, + // #[br(count = header.collision_models)] + // collision_models: Vec, } impl = ()>> Soi { @@ -140,13 +139,13 @@ impl = ()>> Soi { return &self.motion_packs; } - pub fn get_renderable_models(&self) -> &[StreamingRenderableModel] { - return &self.renderable_models; - } + // pub fn get_renderable_models(&self) -> &[StreamingRenderableModel] { + // return &self.renderable_models; + // } - pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { - return &self.collision_models; - } + // pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { + // return &self.collision_models; + // } pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { @@ -195,37 +194,37 @@ impl = ()>> Soi { None } - pub fn find_collision_model( - &self, - section_id: u32, - component_id: u32, - ) -> Option<&StreamingCollisionModel> { - for collision_model in &self.collision_models { - let model_info = &collision_model.model_info; - if model_info.section_id == section_id as i32 - && model_info.component_id == component_id as i32 - { - return Some(&collision_model); - } - } - - None - } - - pub fn find_model( - &self, - section_id: u32, - component_id: u32, - ) -> Option<&StreamingRenderableModel> { - for model in &self.renderable_models { - let model_info = &model.model_info; - if model_info.section_id == section_id as i32 - && model_info.component_id == component_id as i32 - { - return Some(&model); - } - } - - None - } + // pub fn find_collision_model( + // &self, + // section_id: u32, + // component_id: u32, + // ) -> Option<&StreamingCollisionModel> { + // for collision_model in &self.collision_models { + // let model_info = &collision_model.model_info; + // if model_info.section_id == section_id as i32 + // && model_info.component_id == component_id as i32 + // { + // return Some(&collision_model); + // } + // } + + // None + // } + + // pub fn find_model( + // &self, + // section_id: u32, + // component_id: u32, + // ) -> Option<&StreamingRenderableModel> { + // for model in &self.renderable_models { + // let model_info = &model.model_info; + // if model_info.section_id == section_id as i32 + // && model_info.component_id == component_id as i32 + // { + // return Some(&model); + // } + // } + + // None + // } } diff --git a/src/soi_soup.rs b/src/soi_soup.rs index 3cbfffc..aab9465 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -54,13 +54,13 @@ impl = ()>> SoiSoup { self.soi.get_motion_packs() } - pub fn renderable_models(&self) -> &[StreamingRenderableModel] { - self.soi.get_renderable_models() - } - - pub fn collision_models(&self) -> &[StreamingCollisionModel] { - self.soi.get_collision_models() - } + // pub fn renderable_models(&self) -> &[StreamingRenderableModel] { + // self.soi.get_renderable_models() + // } + // + // pub fn collision_models(&self) -> &[StreamingCollisionModel] { + // self.soi.get_collision_models() + // } pub fn component_count(&self) -> u32 { let mut sum = 0; @@ -114,31 +114,31 @@ impl = ()>> SoiSoup { self.soi.find_motion_pack(section_id, component_id) } - pub fn find_collision_model( - &self, - section_id: u32, - component_id: u32, - instance_id: u32, - ) -> Option<&StreamingCollisionModel> { - if let Some(header) = self.soi.find_collision_model(section_id, component_id) { - return Some(header); - } - - let (section_id, component_id) = self.toc.find_ids(instance_id)?; - self.soi.find_collision_model(section_id, component_id) - } - - pub fn find_model( - &self, - section_id: u32, - component_id: u32, - instance_id: u32, - ) -> Option<&StreamingRenderableModel> { - if let Some(header) = self.soi.find_model(section_id, component_id) { - return Some(header); - } - - let (section_id, component_id) = self.toc.find_ids(instance_id)?; - self.soi.find_model(section_id, component_id) - } + // pub fn find_collision_model( + // &self, + // section_id: u32, + // component_id: u32, + // instance_id: u32, + // ) -> Option<&StreamingCollisionModel> { + // if let Some(header) = self.soi.find_collision_model(section_id, component_id) { + // return Some(header); + // } + // + // let (section_id, component_id) = self.toc.find_ids(instance_id)?; + // self.soi.find_collision_model(section_id, component_id) + // } + // + // pub fn find_model( + // &self, + // section_id: u32, + // component_id: u32, + // instance_id: u32, + // ) -> Option<&StreamingRenderableModel> { + // if let Some(header) = self.soi.find_model(section_id, component_id) { + // return Some(header); + // } + // + // let (section_id, component_id) = self.toc.find_ids(instance_id)?; + // self.soi.find_model(section_id, component_id) + // } } diff --git a/src/str.rs b/src/str.rs index 328cf10..2816f33 100644 --- a/src/str.rs +++ b/src/str.rs @@ -44,18 +44,30 @@ impl Str { let section_offset = header.memory_entry.offset as u64; self.file.seek(SeekFrom::Start(section_offset))?; - let uncached = { - let data = - self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; - extract_components(§ion.uncached_components, data) - }; - - let cached = { - let data = self.decode_zlib_data(header.cached_data_size as usize, &zlib.cached_sizes)?; - extract_components(§ion.cached_components, data) - }; - - Ok(SectionData { uncached, cached }) + if !zlib.cached_sizes.is_empty() && !zlib.uncached_sizes.is_empty() { + let uncached = { + let data = + self.decode_zlib_data(header.uncached_data_size as usize, &zlib.uncached_sizes)?; + extract_components(§ion.uncached_components, data) + }; + + let cached = { + let data = self.decode_zlib_data(header.cached_data_size as usize, &zlib.cached_sizes)?; + extract_components(§ion.cached_components, data) + }; + + Ok(SectionData { uncached, cached }) + } else { + let mut uncached_data = vec![0u8; header.uncached_data_size as usize]; + self.file.read(&mut uncached_data); + let uncached = extract_components(§ion.uncached_components, uncached_data); + + let mut cached_data = vec![0u8; header.cached_data_size as usize]; + self.file.read(&mut cached_data); + let cached = extract_components(§ion.cached_components, cached_data); + + Ok(SectionData { uncached, cached }) + } } else { let section_offset = header.memory_entry.offset as u64; self.file.seek(SeekFrom::Start(section_offset))?; diff --git a/src/test.rs b/src/test.rs index f346e8a..797b071 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,37 +1,38 @@ use std::io::Write; use std::path::{Path, PathBuf}; -use binrw::{BinWrite}; +use binrw::BinWrite; use x_flipper_360::*; use crate::utils::*; +use crate::wii_th::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] fn extract() { - let toc_path = Path::new("./data/VehicleInfo.x360.toc"); - let soi_path = Path::new("./data/VehicleInfo.x360.soi"); - let str_path = Path::new("./data/VehicleInfo.x360.str"); + let toc_path = Path::new("./data/CR_03.gcn.toc"); + let soi_path = Path::new("./data/CR_03.gcn.soi"); + let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); for (id, section) in soup.find_sections().iter().enumerate() { let section_data = str.read_section_data(section).unwrap(); - + for component in section_data.uncached { - process_component(&soup, id as u32, component); + process_component_wii(&soup, id as u32, component); } - + for component in section_data.cached { - process_component(&soup, id as u32, component); + process_component_wii(&soup, id as u32, component); } } for static_texture in soup.static_textures().iter() { let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", + ".\\data\\CR_03\\{}.gct", clean_path(&static_texture.model_info.name) )); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); @@ -42,13 +43,10 @@ fn extract() { #[test] fn dump_scn() { - let toc_path = - Path::new("./data/VehicleInfo.x360.toc"); - let soi_path = - Path::new("./data/VehicleInfo.x360.soi"); - let str_path = - Path::new("./data/VehicleInfo.x360.res"); - let soup = SoiSoup::cook(toc_path, soi_path).unwrap(); + let toc_path = Path::new("./data/CR_03.gcn.toc"); + let soi_path = Path::new("./data/CR_03.gcn.soi"); + let str_path = Path::new("./data/CR_03.gcn.str"); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; let mut num_static_models = 1; @@ -80,8 +78,8 @@ fn dump_scn() { } } -fn print_component( - soup: &SoiSoup, +fn print_component = ()> + 'static>( + soup: &SoiSoup, section_id: u32, component: ComponentData, num_anim_models: &mut i32, @@ -89,28 +87,28 @@ fn print_component( num_objects: &mut i32, ) { match component.kind { - RenderableModel => { - let header = soup - .find_model(section_id, component.id, component.instance_id) - .unwrap(); - if header.model_info.is_animated == 1 { - println!("[AnimatedModel{}]\n{}", num_anim_models, header); - *num_anim_models = *num_anim_models + 1; - } else { - println!("[Model{}]\n{}", num_static_models, header); - *num_static_models = *num_static_models + 1; - } - } + // RenderableModel => { + // let header = soup + // .find_model(section_id, component.id, component.instance_id) + // .unwrap(); + // if header.model_info.is_animated == 1 { + // println!("[AnimatedModel{}]\n{}", num_anim_models, header); + // *num_anim_models = *num_anim_models + 1; + // } else { + // println!("[Model{}]\n{}", num_static_models, header); + // *num_static_models = *num_static_models + 1; + // } + // } Texture => { // println!("found Texture component kind; skipping..."); } - CollisionModel => { - let header = soup - .find_collision_model(section_id, component.id, component.instance_id) - .unwrap(); - println!("[Object{}]\n{}", num_objects, header); - *num_objects = *num_objects + 1; - } + // CollisionModel => { + // let header = soup + // .find_collision_model(section_id, component.id, component.instance_id) + // .unwrap(); + // println!("[Object{}]\n{}", num_objects, header); + // *num_objects = *num_objects + 1; + // } UserData => { // println!("found UserData component kind; skipping..."); } @@ -120,70 +118,187 @@ fn print_component( CollisionGrid => { // println!("found CollisionGrid component kind; skipping..."); } + RenderableModel => todo!(), + CollisionModel => todo!(), } } -fn process_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { + +fn process_component_wii(soup: &SoiSoup, section_id: u32, component: ComponentData) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.got", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header.header.write(&mut out); - out.write_all(&component.data); + header.header.write(&mut out).unwrap(); + out.write_all(&component.data).unwrap(); } - if component.kind == ComponentKind::RenderableModel { - let header = soup - .find_model(section_id, component.id, component.instance_id) - .unwrap(); + // if component.kind == ComponentKind::RenderableModel { + // let header = soup + // .find_model(section_id, component.id, component.instance_id) + // .unwrap(); + // + // let path = PathBuf::from(format!( + // ".\\data\\CR_03\\{}.xng", + // component.path + // )); + // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + // let mut out = std::fs::File::create(path).unwrap(); + // header + // .streaming_model_header + // .write_options( + // &mut out, + // binrw::Endian::Big, + // &XNGHeaderArgs { + // streaming_data: component.data.clone(), + // }, + // ) + // .unwrap(); + // } + // if component.kind == ComponentKind::CollisionModel { + // let header = soup + // .find_collision_model(section_id, component.id, component.instance_id) + // .unwrap(); + // + // let path = PathBuf::from(format!( + // ".\\data\\CR_03\\{}.gol", + // component.path + // )); + // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + // let mut out = std::fs::File::create(path).unwrap(); + // header + // .collision_model + // .write_options( + // &mut out, + // binrw::Endian::Big, + // &CollisionModelArgs { + // ror: false, + // streaming_data: component.data.clone(), + // }, + // ) + // .unwrap(); + // } + if component.kind == ComponentKind::Texture { + match soup.find_streaming_texture(section_id, component.id, component.instance_id) { + Some(streaming_texture) => { - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.xng", - component.path - )); - std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - let mut out = std::fs::File::create(path).unwrap(); - header - .streaming_model_header - .write_options( - &mut out, - binrw::Endian::Big, - &XNGHeaderArgs { - streaming_data: component.data.clone(), - }, - ) - .unwrap(); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gct", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + + // Clone the header so we can set the version back to 2 (streamed GCTs have a flag OR'd on the version) + let mut gct_file_header = streaming_texture.header.clone(); + gct_file_header.version = 2; + gct_file_header.write_be(&mut out).unwrap(); + + // Keeps track of our position in the streaming data. + let mut offset = 0; + let mip_count = streaming_texture.header.mip_count; + + // Holds the offsets and sizes (in a Range) for each mip so we can iterate backwards over this later + let mut mips = Vec::with_capacity(mip_count as usize); + + // Since mips are stored in forwards order (biggest to smallest) in the streaming data we need to first collect them in the vec, + for i in 0..mip_count { + let mip_width = 1.max(streaming_texture.header.width as usize / (2 as usize).pow(i)); + let mip_height = 1.max(streaming_texture.header.height as usize / (2 as usize).pow(i)); + + let mip_size = streaming_texture.header.format.calculate_mip_size(mip_width, mip_height); + + mips.push(offset..offset + mip_size); + + offset += mip_size as usize; + } + + // (At this point, we should have read through the entire streaming data) + assert_eq!(offset, streaming_texture.header.calculate_image_size()); + assert_eq!(offset, component.data.len()); + + // and we can then terate over the mips in backwards order and write them to the GCT. + for i in (0..mip_count).rev() { + let mip_width = 1.max(streaming_texture.header.width as usize / (2 as usize).pow(i)); + let mip_height = 1.max(streaming_texture.header.height as usize / (2 as usize).pow(i)); + + let surface_header = GCTSurfaceHeader { + width: mip_width as u32, + height: mip_height as u32, + size: streaming_texture.header.format.calculate_mip_size(mip_width, mip_height) as u32 + }; + surface_header.write_be(&mut out).unwrap(); + + component.data[mips.get(i as usize).unwrap().clone()].write(&mut out).unwrap(); + } + } + None => { + panic!("Failed to find texture header."); + }, + } } - if component.kind == ComponentKind::CollisionModel { +} + + +fn process_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { + if component.kind == ComponentKind::MotionPack { let header = soup - .find_collision_model(section_id, component.id, component.instance_id) + .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.gol", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header - .collision_model - .write_options( - &mut out, - binrw::Endian::Big, - &CollisionModelArgs { - ror: false, - streaming_data: component.data.clone(), - }, - ) - .unwrap(); + header.header.write(&mut out).unwrap(); + out.write_all(&component.data).unwrap(); } + + // if component.kind == ComponentKind::RenderableModel { + // let header = soup + // .find_model(section_id, component.id, component.instance_id) + // .unwrap(); + // + // let path = PathBuf::from(format!( + // ".\\data\\CR_03\\{}.xng", + // component.path + // )); + // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + // let mut out = std::fs::File::create(path).unwrap(); + // header + // .streaming_model_header + // .write_options( + // &mut out, + // binrw::Endian::Big, + // &XNGHeaderArgs { + // streaming_data: component.data.clone(), + // }, + // ) + // .unwrap(); + // } + // if component.kind == ComponentKind::CollisionModel { + // let header = soup + // .find_collision_model(section_id, component.id, component.instance_id) + // .unwrap(); + // + // let path = PathBuf::from(format!( + // ".\\data\\CR_03\\{}.gol", + // component.path + // )); + // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + // let mut out = std::fs::File::create(path).unwrap(); + // header + // .collision_model + // .write_options( + // &mut out, + // binrw::Endian::Big, + // &CollisionModelArgs { + // ror: false, + // streaming_data: component.data.clone(), + // }, + // ) + // .unwrap(); + // } if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(header) => { @@ -207,10 +322,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -232,10 +344,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -256,11 +365,8 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: base_address: metadata.base_address(), mip_address: metadata.mip_address(), }; - - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", - component.path - )); + + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -282,10 +388,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -294,10 +397,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: } None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { - let path = PathBuf::from(format!( - ".\\data\\VehicleInfo\\{}.dds", - component.path - )); + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); out.write_all(&static_texture.header_file).unwrap(); diff --git a/src/wii_th.rs b/src/wii_th.rs new file mode 100644 index 0000000..1b008ad --- /dev/null +++ b/src/wii_th.rs @@ -0,0 +1,107 @@ +use binrw::*; + +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[brw(repr = u32)] +pub(crate) enum GCTFormat { + Rgba8 = 0x0F, + Cmpr = 0x29, + Cmpr_MM = 0x2A, + Ci8 = 0x3A, + Ci8_MM = 0x3B, + I8 = 0x3C, +} + +fn round_up(numToRound: usize, roundTo: usize) -> usize { + return ((numToRound + roundTo - 1) / roundTo) * roundTo; +} + +fn div_round_up(numToRound: usize, roundTo: usize) -> usize { + return (numToRound + roundTo - 1) / roundTo; +} + +impl GCTFormat { + pub fn get_block_dim(&self) -> (usize, usize) { + match self { + GCTFormat::Rgba8 => (4, 4), + GCTFormat::Cmpr => (8, 8), + GCTFormat::Cmpr_MM => (8, 8), + GCTFormat::Ci8 => (8, 4), + GCTFormat::Ci8_MM => (8, 4), + GCTFormat::I8 => (8, 4), + } + } + pub fn get_bits_per_pixel(&self) -> usize { + match self { + GCTFormat::Rgba8 => 32, + GCTFormat::Cmpr => 4, + GCTFormat::Cmpr_MM => 4, + GCTFormat::Ci8 => 8, + GCTFormat::Ci8_MM => 8, + GCTFormat::I8 => 8, + } + } + pub fn calculate_mip_size(&self, width: usize, height: usize) -> usize { + let (blk_width_pixels, blk_height_pixels) = self.get_block_dim(); + let bits_per_pixel = self.get_bits_per_pixel(); + let blk_size_bytes = ((blk_width_pixels * blk_height_pixels) * bits_per_pixel) / 8; + let size_bytes = div_round_up(width, blk_width_pixels) + * div_round_up(height, blk_height_pixels) + * blk_size_bytes; + + size_bytes + } +} + +impl std::fmt::Display for GCTFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GCTFormat::Rgba8 => write!(f, "Rgba8"), + GCTFormat::Cmpr | GCTFormat::Cmpr_MM => write!(f, "Cmpr"), + GCTFormat::Ci8 | GCTFormat::Ci8_MM => write!(f, "Ci8"), + GCTFormat::I8 => write!(f, "I8"), + } + } +} + +#[derive(BinRead, BinWrite, Debug, Clone)] +pub(crate) struct GCNTextureHeader { + pub version: u32, + pub format: GCTFormat, + pub palette_size: u32, + + #[br(count = palette_size * 2)] + pub palette: Vec, + + pub mip_count: u32, + pub width: u32, + pub height: u32, +} + +#[derive(BinRead, BinWrite, Debug)] +pub(crate) struct GCTSurfaceHeader { + pub width: u32, + pub height: u32, + pub size: u32, +} + +impl GCNTextureHeader { + pub fn calculate_image_size(&self) -> usize { + let (blk_width_pixels, blk_height_pixels) = self.format.get_block_dim(); + let bits_per_pixel = self.format.get_bits_per_pixel(); + let blk_size_bytes = ((blk_width_pixels * blk_height_pixels) * bits_per_pixel) / 8; + let mut size_bytes = div_round_up(self.width as usize, blk_width_pixels) + * div_round_up(self.height as usize, blk_height_pixels) + * blk_size_bytes; + + for i in 1..self.mip_count { + let mip_width = self.width as usize / (2 as usize).pow(i); + let mip_height = self.height as usize / (2 as usize).pow(i); + + size_bytes += div_round_up(mip_width as usize, blk_width_pixels) + * div_round_up(mip_height as usize, blk_height_pixels) + * blk_size_bytes; + } + + size_bytes + } +} \ No newline at end of file From 54416fe3b3af4bd4abe11330be3c734418844c8f Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 14 Jun 2023 19:17:59 -0400 Subject: [PATCH 13/23] move texture stuff into folder --- src/lib.rs | 3 ++- src/models/gcg.rs | 0 src/test.rs | 2 +- src/{wii_th.rs => textures/gct.rs} | 0 src/textures/mod.rs | 2 ++ 5 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 src/models/gcg.rs rename src/{wii_th.rs => textures/gct.rs} (100%) create mode 100644 src/textures/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 5071366..27c0d84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub use crate::collision::*; pub use crate::models::*; +pub use crate::textures::*; pub use crate::motion::*; pub use crate::soi::*; pub use crate::soi_soup::*; @@ -15,7 +16,7 @@ mod soi_soup; mod str; mod toc; mod utils; -mod wii_th; +mod textures; #[cfg(test)] mod test; diff --git a/src/models/gcg.rs b/src/models/gcg.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/test.rs b/src/test.rs index 797b071..69e1370 100644 --- a/src/test.rs +++ b/src/test.rs @@ -5,7 +5,7 @@ use binrw::BinWrite; use x_flipper_360::*; use crate::utils::*; -use crate::wii_th::{GCNTextureHeader, GCTSurfaceHeader}; +use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; diff --git a/src/wii_th.rs b/src/textures/gct.rs similarity index 100% rename from src/wii_th.rs rename to src/textures/gct.rs diff --git a/src/textures/mod.rs b/src/textures/mod.rs new file mode 100644 index 0000000..b877089 --- /dev/null +++ b/src/textures/mod.rs @@ -0,0 +1,2 @@ +mod gct; +pub use self::gct::*; \ No newline at end of file From 6ac7374cc70f7764e942da5e433d2e2017ca40d9 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 14 Jun 2023 23:14:12 -0400 Subject: [PATCH 14/23] streaming gcg parsing --- src/lib.rs | 4 +- src/models/gcg.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++ src/models/mod.rs | 65 ++++++++++++++++++++++++- src/models/xng.rs | 66 ++------------------------ src/soi.rs | 91 +++++++++++++++++------------------ src/soi_soup.rs | 77 +++++++++++++++--------------- src/str.rs | 4 +- src/test.rs | 104 +++++++++++++++++++++------------------- src/textures/gct.rs | 26 +++++----- src/textures/mod.rs | 2 +- 10 files changed, 340 insertions(+), 211 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 27c0d84..bb34d05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,10 @@ pub use crate::collision::*; pub use crate::models::*; -pub use crate::textures::*; pub use crate::motion::*; pub use crate::soi::*; pub use crate::soi_soup::*; pub use crate::str::*; +pub use crate::textures::*; pub use crate::toc::*; pub use crate::utils::*; @@ -14,9 +14,9 @@ mod motion; mod soi; mod soi_soup; mod str; +mod textures; mod toc; mod utils; -mod textures; #[cfg(test)] mod test; diff --git a/src/models/gcg.rs b/src/models/gcg.rs index e69de29..fb921a7 100644 --- a/src/models/gcg.rs +++ b/src/models/gcg.rs @@ -0,0 +1,112 @@ +use binrw::{BinRead, BinWrite}; + +use crate::{Bone, MeshName}; + +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[brw(repr = u8)] +pub enum GXCompType { + GX_U8 = 0, + GX_S8 = 1, + GX_U16 = 2, + GX_S16 = 3, + GX_F32 = 4, + GX_U32 = 5, +} + +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[brw(repr = u8)] +pub enum GXAttrType { + GX_NONE = 0, + GX_DIRECT = 1, + GX_INDEX8 = 2, + GX_INDEX16 = 3, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct StreamingGCGMesh { + pub surface_index: u32, + pub vertex_type: u8, + + pub vertex_attr_type: GXAttrType, + pub vertex_data_type: GXCompType, + pub xyz_frac_bits: u8, + + #[br(if ((vertex_type & 0x1) == 0x1 && (vertex_type & 0x8) == 0x8))] + pub normal_attr_type: Option, + #[br(if ((vertex_type & 0x1) == 0x1 && (vertex_type & 0x8) == 0x8))] + pub normal_data_type: Option, + + #[br(if ((vertex_type & 0x1) == 0x0))] + pub color_attr_type: Option, + #[br(if ((vertex_type & 0x1) == 0x0))] + pub color_data_type: Option, + + pub uv_attr_type: GXAttrType, + pub uv_data_type: GXCompType, + pub tex_frac_bits: u8, + + pub vertex_count: u16, + pub byte_stride: u8, + + #[br(if ((vertex_type & 0x1) == 0x1 && (vertex_type & 0x8) == 0x8))] + pub normal_count: u16, + #[br(if ((vertex_type & 0x1) == 0x1 && (vertex_type & 0x8) == 0x8))] + pub normal_stride: u8, + + #[br(if ((vertex_type & 0x1) == 0x0))] + pub color_count: u16, + #[br(if ((vertex_type & 0x1) == 0x0))] + pub color_stride: u8, + + pub uv_count: u16, + pub uv_stride: u8, + pub face_chunk_size: i32, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct GCGLod { + pub auto_lod_value: f32, + pub num_meshes: u32, + + #[br(count = num_meshes)] + pub meshes: Vec, +} + +#[derive(BinRead, Debug)] +#[br(big)] +pub struct GCGWeight { + pub bone_id: u16, + pub weight: f32, +} + +#[derive(BinRead, Debug)] +#[br(big)] +#[br(magic = b"ggs\0")] +pub struct GCGHeader { + version: i32, + num_bones: u32, + + #[br(count = num_bones)] + bones: Vec, + + num_mesh_names: i32, + + #[br(count = num_mesh_names)] + pub mesh_names: Vec, + + pub num_lod: u8, + skin_animates_flag: u8, + has_weight: u8, + unused: u8, + + #[br(if(has_weight != 0))] + weight_count: u16, + + #[br(if(has_weight != 0), count = weight_count)] + pub weights: Option>, + + #[br(count = num_lod)] + pub lods: Vec, +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 6acf3f0..e75392c 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1,65 @@ +mod gcg; mod xng; -pub use self::xng::*; \ No newline at end of file +use binrw::BinRead; +use binrw::BinWrite; + +use crate::clean_string; + +pub use self::gcg::*; +pub use self::xng::*; + +#[derive(BinRead, Debug)] +pub struct StreamingRenderableModel = ()> + 'static> { + pub model_info: crate::ModelInfo, + + #[br(count = model_info.parameter_count)] + pub parameters: Vec, + + pub streaming_model_header: MH, +} + +impl = ()>> std::fmt::Display for StreamingRenderableModel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.model_info.zone != -1 { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + self.model_info.zone + ); + } else { + write!( + f, + "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", + clean_string(&self.model_info.name), + self.model_info.position, + self.model_info.look_vector, + self.model_info.up_vector, + ); + } + Ok(for param in self.parameters.iter() { + write!(f, "{}\n", param); + }) + } +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct MeshName { + #[br(count = 64)] + name: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(big)] +pub struct Bone { + name: [u8; 128], + matrix: [f32; 16], + bounding_box_center: [f32; 3], + bounding_box_half: [f32; 3], + bounding_box_radius: f32, + parent_index: u32, +} diff --git a/src/models/xng.rs b/src/models/xng.rs index e32a10d..5f68a30 100644 --- a/src/models/xng.rs +++ b/src/models/xng.rs @@ -1,25 +1,7 @@ use binrw::{BinRead, BinResult, BinWrite, Endian}; use std::io::{Seek, Write}; -use crate::utils::*; - -#[derive(BinRead, BinWrite, Debug)] -#[brw(big)] -pub struct XNGMeshName { - #[br(count = 64)] - name: Vec, -} - -#[derive(BinRead, BinWrite, Debug)] -#[brw(big)] -pub struct XNGBone { - name: [u8; 128], - matrix: [f32; 16], - bounding_box_center: [f32; 3], - bounding_box_half: [f32; 3], - bounding_box_radius: f32, - parent_index: u32, -} +use crate::{utils::*, Bone, MeshName}; #[derive(Default, BinRead, BinWrite, Debug)] #[brw(big)] @@ -97,12 +79,12 @@ pub struct XNGHeader { num_bones: u32, #[br(count = num_bones)] - bones: Vec, + bones: Vec, num_mesh_names: i32, #[br(count = num_mesh_names)] - pub mesh_names: Vec, + pub mesh_names: Vec, pub num_lod: u8, skin_animates_flag: u8, @@ -113,44 +95,6 @@ pub struct XNGHeader { pub lods: Vec, } -#[derive(BinRead, Debug)] -pub struct StreamingRenderableModel { - pub model_info: crate::ModelInfo, - - #[br(count = model_info.parameter_count)] - pub parameters: Vec, - - pub streaming_model_header: XNGHeader, -} - -impl std::fmt::Display for StreamingRenderableModel { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.model_info.zone != -1 { - write!( - f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\nZone={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - self.model_info.zone - ); - } else { - write!( - f, - "SLT={}\nPosition={}\nLookVector={}\nUpVector={}\n", - clean_string(&self.model_info.name), - self.model_info.position, - self.model_info.look_vector, - self.model_info.up_vector, - ); - } - Ok(for param in self.parameters.iter() { - write!(f, "{}\n", param); - }) - } -} - // BinrwNamedArgs #[derive(Clone, Debug)] pub struct XNGHeaderArgs { @@ -172,9 +116,9 @@ impl BinWrite for XNGHeader { Vec::::write_options(&magic, writer, endian, ())?; i32::write_options(&self.version, writer, endian, ())?; u32::write_options(&self.num_bones, writer, endian, ())?; - Vec::::write_options(&self.bones, writer, endian, ())?; + Vec::::write_options(&self.bones, writer, endian, ())?; i32::write_options(&self.num_mesh_names, writer, endian, ())?; - Vec::::write_options(&self.mesh_names, writer, endian, ())?; + Vec::::write_options(&self.mesh_names, writer, endian, ())?; u8::write_options(&self.num_lod, writer, endian, ())?; u8::write_options(&self.skin_animates_flag, writer, endian, ())?; u8::write_options(&self.has_weight, writer, endian, ())?; diff --git a/src/soi.rs b/src/soi.rs index 67cf02f..a890069 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -91,7 +91,8 @@ pub struct StaticTexture { } #[derive(BinRead, Debug)] -pub struct Soi = ()> + 'static> { +pub struct Soi = ()> + 'static, MH: BinRead = ()> + 'static> +{ pub header: Header, #[br(count = header.uncached_pages)] @@ -110,14 +111,14 @@ pub struct Soi = ()> + 'static> { motion_packs: Vec, // #[br(if(header.flags & 64 == 1))] // collision_grid_info: StreamingCollisionGridInfo, - // #[br(count = header.renderable_models)] - // renderable_models: Vec, + #[br(count = header.renderable_models)] + renderable_models: Vec>, - // #[br(count = header.collision_models)] - // collision_models: Vec, + #[br(count = header.collision_models)] + collision_models: Vec, } -impl = ()>> Soi { +impl = ()>, MH: BinRead = ()>> Soi { pub fn read(path: &Path) -> BinResult { let mut file = File::open(path)?; Self::read_file(&mut file) @@ -139,13 +140,13 @@ impl = ()>> Soi { return &self.motion_packs; } - // pub fn get_renderable_models(&self) -> &[StreamingRenderableModel] { - // return &self.renderable_models; - // } + pub fn get_renderable_models(&self) -> &[StreamingRenderableModel] { + return &self.renderable_models; + } - // pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { - // return &self.collision_models; - // } + pub fn get_collision_models(&self) -> &[StreamingCollisionModel] { + return &self.collision_models; + } pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { @@ -194,37 +195,37 @@ impl = ()>> Soi { None } - // pub fn find_collision_model( - // &self, - // section_id: u32, - // component_id: u32, - // ) -> Option<&StreamingCollisionModel> { - // for collision_model in &self.collision_models { - // let model_info = &collision_model.model_info; - // if model_info.section_id == section_id as i32 - // && model_info.component_id == component_id as i32 - // { - // return Some(&collision_model); - // } - // } - - // None - // } - - // pub fn find_model( - // &self, - // section_id: u32, - // component_id: u32, - // ) -> Option<&StreamingRenderableModel> { - // for model in &self.renderable_models { - // let model_info = &model.model_info; - // if model_info.section_id == section_id as i32 - // && model_info.component_id == component_id as i32 - // { - // return Some(&model); - // } - // } - - // None - // } + pub fn find_collision_model( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StreamingCollisionModel> { + for collision_model in &self.collision_models { + let model_info = &collision_model.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&collision_model); + } + } + + None + } + + pub fn find_model( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StreamingRenderableModel> { + for model in &self.renderable_models { + let model_info = &model.model_info; + if model_info.section_id == section_id as i32 + && model_info.component_id == component_id as i32 + { + return Some(&model); + } + } + + None + } } diff --git a/src/soi_soup.rs b/src/soi_soup.rs index aab9465..ff1681e 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -7,12 +7,15 @@ use crate::{ StreamingRenderableModel, StreamingTexture, Toc, }; -pub struct SoiSoup = ()> + 'static> { +pub struct SoiSoup< + TH: BinRead = ()> + 'static, + MH: BinRead = ()> + 'static, +> { toc: Toc, - soi: Soi, + soi: Soi, } -impl = ()>> SoiSoup { +impl = ()>, MH: BinRead = ()>> SoiSoup { pub fn cook(toc_path: &Path, soi_path: &Path) -> BinResult { let soi = Soi::read(soi_path)?; let toc = Toc::read(toc_path, soi.header.version == 0x101)?; @@ -54,13 +57,13 @@ impl = ()>> SoiSoup { self.soi.get_motion_packs() } - // pub fn renderable_models(&self) -> &[StreamingRenderableModel] { - // self.soi.get_renderable_models() - // } - // - // pub fn collision_models(&self) -> &[StreamingCollisionModel] { - // self.soi.get_collision_models() - // } + pub fn renderable_models(&self) -> &[StreamingRenderableModel] { + self.soi.get_renderable_models() + } + + pub fn collision_models(&self) -> &[StreamingCollisionModel] { + self.soi.get_collision_models() + } pub fn component_count(&self) -> u32 { let mut sum = 0; @@ -114,31 +117,31 @@ impl = ()>> SoiSoup { self.soi.find_motion_pack(section_id, component_id) } - // pub fn find_collision_model( - // &self, - // section_id: u32, - // component_id: u32, - // instance_id: u32, - // ) -> Option<&StreamingCollisionModel> { - // if let Some(header) = self.soi.find_collision_model(section_id, component_id) { - // return Some(header); - // } - // - // let (section_id, component_id) = self.toc.find_ids(instance_id)?; - // self.soi.find_collision_model(section_id, component_id) - // } - // - // pub fn find_model( - // &self, - // section_id: u32, - // component_id: u32, - // instance_id: u32, - // ) -> Option<&StreamingRenderableModel> { - // if let Some(header) = self.soi.find_model(section_id, component_id) { - // return Some(header); - // } - // - // let (section_id, component_id) = self.toc.find_ids(instance_id)?; - // self.soi.find_model(section_id, component_id) - // } + pub fn find_collision_model( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&StreamingCollisionModel> { + if let Some(header) = self.soi.find_collision_model(section_id, component_id) { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self.soi.find_collision_model(section_id, component_id) + } + + pub fn find_model( + &self, + section_id: u32, + component_id: u32, + instance_id: u32, + ) -> Option<&StreamingRenderableModel> { + if let Some(header) = self.soi.find_model(section_id, component_id) { + return Some(header); + } + + let (section_id, component_id) = self.toc.find_ids(instance_id)?; + self.soi.find_model(section_id, component_id) + } } diff --git a/src/str.rs b/src/str.rs index 2816f33..39412ad 100644 --- a/src/str.rs +++ b/src/str.rs @@ -61,11 +61,11 @@ impl Str { let mut uncached_data = vec![0u8; header.uncached_data_size as usize]; self.file.read(&mut uncached_data); let uncached = extract_components(§ion.uncached_components, uncached_data); - + let mut cached_data = vec![0u8; header.cached_data_size as usize]; self.file.read(&mut cached_data); let cached = extract_components(§ion.cached_components, cached_data); - + Ok(SectionData { uncached, cached }) } } else { diff --git a/src/test.rs b/src/test.rs index 69e1370..ed04d8f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,9 +4,9 @@ use std::path::{Path, PathBuf}; use binrw::BinWrite; use x_flipper_360::*; -use crate::utils::*; use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; +use crate::{utils::*, GCGHeader, XNGHeader}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] @@ -15,30 +15,22 @@ fn extract() { let soi_path = Path::new("./data/CR_03.gcn.soi"); let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); + /* for (id, section) in soup.find_sections().iter().enumerate() { let section_data = str.read_section_data(section).unwrap(); - + for component in section_data.uncached { process_component_wii(&soup, id as u32, component); } - + for component in section_data.cached { process_component_wii(&soup, id as u32, component); } } - - for static_texture in soup.static_textures().iter() { - let path = PathBuf::from(format!( - ".\\data\\CR_03\\{}.gct", - clean_path(&static_texture.model_info.name) - )); - std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - let mut out = std::fs::File::create(path).unwrap(); - out.write_all(&static_texture.header_file).unwrap(); - } + */ } #[test] @@ -46,7 +38,7 @@ fn dump_scn() { let toc_path = Path::new("./data/CR_03.gcn.toc"); let soi_path = Path::new("./data/CR_03.gcn.soi"); let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; let mut num_static_models = 1; @@ -78,8 +70,11 @@ fn dump_scn() { } } -fn print_component = ()> + 'static>( - soup: &SoiSoup, +fn print_component< + TH: binrw::BinRead = ()> + 'static, + MH: binrw::BinRead = ()> + 'static, +>( + soup: &SoiSoup, section_id: u32, component: ComponentData, num_anim_models: &mut i32, @@ -87,28 +82,28 @@ fn print_component = ()> + 'static>( num_objects: &mut i32, ) { match component.kind { - // RenderableModel => { - // let header = soup - // .find_model(section_id, component.id, component.instance_id) - // .unwrap(); - // if header.model_info.is_animated == 1 { - // println!("[AnimatedModel{}]\n{}", num_anim_models, header); - // *num_anim_models = *num_anim_models + 1; - // } else { - // println!("[Model{}]\n{}", num_static_models, header); - // *num_static_models = *num_static_models + 1; - // } - // } + RenderableModel => { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + if header.model_info.is_animated == 1 { + println!("[AnimatedModel{}]\n{}", num_anim_models, header); + *num_anim_models = *num_anim_models + 1; + } else { + println!("[Model{}]\n{}", num_static_models, header); + *num_static_models = *num_static_models + 1; + } + } Texture => { // println!("found Texture component kind; skipping..."); } - // CollisionModel => { - // let header = soup - // .find_collision_model(section_id, component.id, component.instance_id) - // .unwrap(); - // println!("[Object{}]\n{}", num_objects, header); - // *num_objects = *num_objects + 1; - // } + CollisionModel => { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + println!("[Object{}]\n{}", num_objects, header); + *num_objects = *num_objects + 1; + } UserData => { // println!("found UserData component kind; skipping..."); } @@ -118,13 +113,14 @@ fn print_component = ()> + 'static>( CollisionGrid => { // println!("found CollisionGrid component kind; skipping..."); } - RenderableModel => todo!(), - CollisionModel => todo!(), } } - -fn process_component_wii(soup: &SoiSoup, section_id: u32, component: ComponentData) { +fn process_component_wii( + soup: &SoiSoup, + section_id: u32, + component: ComponentData, +) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) @@ -185,7 +181,6 @@ fn process_component_wii(soup: &SoiSoup, section_id: u32, comp if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(streaming_texture) => { - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gct", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); @@ -195,7 +190,7 @@ fn process_component_wii(soup: &SoiSoup, section_id: u32, comp gct_file_header.version = 2; gct_file_header.write_be(&mut out).unwrap(); - // Keeps track of our position in the streaming data. + // Keeps track of our position in the streaming data. let mut offset = 0; let mip_count = streaming_texture.header.mip_count; @@ -207,7 +202,10 @@ fn process_component_wii(soup: &SoiSoup, section_id: u32, comp let mip_width = 1.max(streaming_texture.header.width as usize / (2 as usize).pow(i)); let mip_height = 1.max(streaming_texture.header.height as usize / (2 as usize).pow(i)); - let mip_size = streaming_texture.header.format.calculate_mip_size(mip_width, mip_height); + let mip_size = streaming_texture + .header + .format + .calculate_mip_size(mip_width, mip_height); mips.push(offset..offset + mip_size); @@ -226,22 +224,30 @@ fn process_component_wii(soup: &SoiSoup, section_id: u32, comp let surface_header = GCTSurfaceHeader { width: mip_width as u32, height: mip_height as u32, - size: streaming_texture.header.format.calculate_mip_size(mip_width, mip_height) as u32 + size: streaming_texture + .header + .format + .calculate_mip_size(mip_width, mip_height) as u32, }; surface_header.write_be(&mut out).unwrap(); - component.data[mips.get(i as usize).unwrap().clone()].write(&mut out).unwrap(); + component.data[mips.get(i as usize).unwrap().clone()] + .write(&mut out) + .unwrap(); } } None => { panic!("Failed to find texture header."); - }, + } } } } - -fn process_component(soup: &SoiSoup, section_id: u32, component: ComponentData) { +fn process_component( + soup: &SoiSoup, + section_id: u32, + component: ComponentData, +) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) @@ -365,7 +371,7 @@ fn process_component(soup: &SoiSoup, section_id: u32, component: base_address: metadata.base_address(), mip_address: metadata.mip_address(), }; - + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); diff --git a/src/textures/gct.rs b/src/textures/gct.rs index 1b008ad..00e5262 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -47,20 +47,20 @@ impl GCTFormat { let size_bytes = div_round_up(width, blk_width_pixels) * div_round_up(height, blk_height_pixels) * blk_size_bytes; - + size_bytes } } impl std::fmt::Display for GCTFormat { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - GCTFormat::Rgba8 => write!(f, "Rgba8"), - GCTFormat::Cmpr | GCTFormat::Cmpr_MM => write!(f, "Cmpr"), - GCTFormat::Ci8 | GCTFormat::Ci8_MM => write!(f, "Ci8"), - GCTFormat::I8 => write!(f, "I8"), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GCTFormat::Rgba8 => write!(f, "Rgba8"), + GCTFormat::Cmpr | GCTFormat::Cmpr_MM => write!(f, "Cmpr"), + GCTFormat::Ci8 | GCTFormat::Ci8_MM => write!(f, "Ci8"), + GCTFormat::I8 => write!(f, "I8"), } + } } #[derive(BinRead, BinWrite, Debug, Clone)] @@ -71,7 +71,7 @@ pub(crate) struct GCNTextureHeader { #[br(count = palette_size * 2)] pub palette: Vec, - + pub mip_count: u32, pub width: u32, pub height: u32, @@ -92,16 +92,16 @@ impl GCNTextureHeader { let mut size_bytes = div_round_up(self.width as usize, blk_width_pixels) * div_round_up(self.height as usize, blk_height_pixels) * blk_size_bytes; - + for i in 1..self.mip_count { let mip_width = self.width as usize / (2 as usize).pow(i); let mip_height = self.height as usize / (2 as usize).pow(i); size_bytes += div_round_up(mip_width as usize, blk_width_pixels) - * div_round_up(mip_height as usize, blk_height_pixels) - * blk_size_bytes; + * div_round_up(mip_height as usize, blk_height_pixels) + * blk_size_bytes; } size_bytes } -} \ No newline at end of file +} diff --git a/src/textures/mod.rs b/src/textures/mod.rs index b877089..1741c64 100644 --- a/src/textures/mod.rs +++ b/src/textures/mod.rs @@ -1,2 +1,2 @@ mod gct; -pub use self::gct::*; \ No newline at end of file +pub use self::gct::*; From d6a70ab64d02e3a1191914ca8ba7238a6af68fd4 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 15 Jun 2023 01:58:19 -0400 Subject: [PATCH 15/23] wip gcg writing --- src/models/gcg.rs | 144 ++++++++++++++++++++++++++++++++++-- src/test.rs | 174 ++++++++++++++++++++------------------------ src/textures/gct.rs | 10 +-- src/utils.rs | 8 ++ 4 files changed, 228 insertions(+), 108 deletions(-) diff --git a/src/models/gcg.rs b/src/models/gcg.rs index fb921a7..1478050 100644 --- a/src/models/gcg.rs +++ b/src/models/gcg.rs @@ -1,8 +1,11 @@ -use binrw::{BinRead, BinWrite}; +use core::num; +use std::io::{Seek, Write}; + +use binrw::{BinRead, BinResult, BinWrite, Endian}; use crate::{Bone, MeshName}; -#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone, Copy)] #[brw(repr = u8)] pub enum GXCompType { GX_U8 = 0, @@ -13,7 +16,7 @@ pub enum GXCompType { GX_U32 = 5, } -#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone, Copy)] #[brw(repr = u8)] pub enum GXAttrType { GX_NONE = 0, @@ -38,7 +41,7 @@ pub struct StreamingGCGMesh { pub normal_data_type: Option, #[br(if ((vertex_type & 0x1) == 0x0))] - pub color_attr_type: Option, + pub color_attr_type: Option, #[br(if ((vertex_type & 0x1) == 0x0))] pub color_data_type: Option, @@ -61,7 +64,7 @@ pub struct StreamingGCGMesh { pub uv_count: u16, pub uv_stride: u8, - pub face_chunk_size: i32, + pub face_chunk_size: u32, } #[derive(BinRead, BinWrite, Debug)] @@ -74,7 +77,7 @@ pub struct GCGLod { pub meshes: Vec, } -#[derive(BinRead, Debug)] +#[derive(BinRead, BinWrite, Debug, Clone, Copy)] #[br(big)] pub struct GCGWeight { pub bone_id: u16, @@ -110,3 +113,132 @@ pub struct GCGHeader { #[br(count = num_lod)] pub lods: Vec, } + +// BinrwNamedArgs +#[derive(Clone, Debug)] +pub struct GCGHeaderArgs { + pub streaming_data: Vec, +} + +// This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper XNG file. +// As such, the streaming data must be passed to write_options. +impl BinWrite for GCGHeader { + type Args<'a> = &'a GCGHeaderArgs; + + fn write_options( + &self, + writer: &mut W, + endian: Endian, + args: Self::Args<'_>, + ) -> BinResult<()> { + let magic = b"gcg\0".to_vec(); + Vec::::write_options(&magic, writer, endian, ())?; + i32::write_options(&self.version, writer, endian, ())?; + u32::write_options(&self.num_bones, writer, endian, ())?; + Vec::::write_options(&self.bones, writer, endian, ())?; + i32::write_options(&self.num_mesh_names, writer, endian, ())?; + Vec::::write_options(&self.mesh_names, writer, endian, ())?; + u8::write_options(&self.num_lod, writer, endian, ())?; + u8::write_options(&self.skin_animates_flag, writer, endian, ())?; + u8::write_options(&self.has_weight, writer, endian, ())?; + if self.has_weight != 0 { + Vec::::write_options(&self.weights.clone().unwrap(), writer, endian, ())?; + } + + let mut offset_in_data: usize = 0; + + for lod in &self.lods { + f32::write_options(&lod.auto_lod_value, writer, endian, ())?; + u32::write_options(&lod.num_meshes, writer, endian, ())?; + for mesh in &lod.meshes { + u32::write_options(&mesh.surface_index, writer, endian, ())?; + u8::write_options(&(mesh.vertex_type - 0x80), writer, endian, ())?; + GXAttrType::write_options(&mesh.vertex_attr_type, writer, endian, ())?; + GXCompType::write_options(&mesh.vertex_data_type, writer, endian, ())?; + + if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { + GXAttrType::write_options(&mesh.normal_attr_type.unwrap(), writer, endian, ())?; + GXCompType::write_options(&mesh.normal_data_type.unwrap(), writer, endian, ())?; + } + + if (mesh.vertex_type & 0x1) == 0x0 { + GXAttrType::write_options(&mesh.color_attr_type.unwrap(), writer, endian, ())?; + GXCompType::write_options(&mesh.color_data_type.unwrap(), writer, endian, ())?; + } + + GXAttrType::write_options(&mesh.uv_attr_type, writer, endian, ())?; + GXCompType::write_options(&mesh.uv_data_type, writer, endian, ())?; + u8::write_options(&mesh.tex_frac_bits, writer, endian, ())?; + + u16::write_options(&mesh.vertex_count, writer, endian, ())?; + u8::write_options(&mesh.byte_stride, writer, endian, ())?; + + if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { + u16::write_options(&mesh.normal_count, writer, endian, ())?; + u8::write_options(&mesh.normal_stride, writer, endian, ())?; + } + + if (mesh.vertex_type & 0x1) == 0x0 { + u16::write_options(&mesh.color_count, writer, endian, ())?; + u8::write_options(&mesh.color_stride, writer, endian, ())?; + } + + u16::write_options(&mesh.uv_count, writer, endian, ())?; + u8::write_options(&mesh.uv_stride, writer, endian, ())?; + + u32::write_options(&mesh.face_chunk_size, writer, endian, ())?; + let mut vertex_block_size = 0; + + vertex_block_size += match mesh.vertex_data_type { + GXCompType::GX_U8 => mesh.vertex_count as usize * 3, + GXCompType::GX_S8 => mesh.vertex_count as usize * 3, + GXCompType::GX_U16 => mesh.vertex_count as usize * 6, + GXCompType::GX_S16 => mesh.vertex_count as usize * 6, + GXCompType::GX_F32 => mesh.vertex_count as usize * 12, + GXCompType::GX_U32 => mesh.vertex_count as usize * 12, + }; + + if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { + vertex_block_size += match mesh.normal_data_type { + Some(GXCompType::GX_U8) => mesh.normal_count as usize * 3, + Some(GXCompType::GX_S8) => mesh.normal_count as usize * 3, + Some(GXCompType::GX_U16) => mesh.normal_count as usize * 6, + Some(GXCompType::GX_S16) => mesh.normal_count as usize * 6, + Some(GXCompType::GX_F32) => mesh.normal_count as usize * 12, + Some(GXCompType::GX_U32) => mesh.normal_count as usize * 12, + None => 0, + }; + } + + if (mesh.vertex_type & 0x1) == 0x0 { + vertex_block_size += 4 * mesh.color_count as usize; + } + + if (mesh.vertex_type & 0x2) == 0x2 { + vertex_block_size += match mesh.uv_data_type { + GXCompType::GX_U8 => mesh.uv_count as usize * 2, + GXCompType::GX_S8 => mesh.uv_count as usize * 2, + GXCompType::GX_U16 => mesh.uv_count as usize * 4, + GXCompType::GX_S16 => mesh.uv_count as usize * 4, + GXCompType::GX_F32 => mesh.uv_count as usize * 8, + GXCompType::GX_U32 => mesh.uv_count as usize * 8, + }; + } + + // write the vertex block (positions, normals or vertex colors, uvs) + let vertex_block_end_offset = crate::round_up(offset_in_data + vertex_block_size, 32); + let vertex_block = (&args.streaming_data[offset_in_data..vertex_block_end_offset]).to_vec(); + Vec::::write_options(&vertex_block, writer, endian, ())?; + offset_in_data = vertex_block_end_offset; + + let face_chunk: Vec = (&args.streaming_data + [offset_in_data..offset_in_data + mesh.face_chunk_size as usize]) + .to_vec(); + Vec::::write_options(&face_chunk, writer, endian, ())?; + offset_in_data += mesh.face_chunk_size as usize; + } + println!("{}, {}", offset_in_data, args.streaming_data.len()); + } + Ok(()) + } +} diff --git a/src/test.rs b/src/test.rs index ed04d8f..0dfe7a8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,7 +6,7 @@ use x_flipper_360::*; use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; -use crate::{utils::*, GCGHeader, XNGHeader}; +use crate::{utils::*, GCGHeader, GCGHeaderArgs, XNGHeader}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] @@ -18,7 +18,6 @@ fn extract() { let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); - /* for (id, section) in soup.find_sections().iter().enumerate() { let section_data = str.read_section_data(section).unwrap(); @@ -30,7 +29,6 @@ fn extract() { process_component_wii(&soup, id as u32, component); } } - */ } #[test] @@ -133,51 +131,45 @@ fn process_component_wii( out.write_all(&component.data).unwrap(); } - // if component.kind == ComponentKind::RenderableModel { - // let header = soup - // .find_model(section_id, component.id, component.instance_id) - // .unwrap(); - // - // let path = PathBuf::from(format!( - // ".\\data\\CR_03\\{}.xng", - // component.path - // )); - // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - // let mut out = std::fs::File::create(path).unwrap(); - // header - // .streaming_model_header - // .write_options( - // &mut out, - // binrw::Endian::Big, - // &XNGHeaderArgs { - // streaming_data: component.data.clone(), - // }, - // ) - // .unwrap(); - // } - // if component.kind == ComponentKind::CollisionModel { - // let header = soup - // .find_collision_model(section_id, component.id, component.instance_id) - // .unwrap(); - // - // let path = PathBuf::from(format!( - // ".\\data\\CR_03\\{}.gol", - // component.path - // )); - // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - // let mut out = std::fs::File::create(path).unwrap(); - // header - // .collision_model - // .write_options( - // &mut out, - // binrw::Endian::Big, - // &CollisionModelArgs { - // ror: false, - // streaming_data: component.data.clone(), - // }, - // ) - // .unwrap(); - // } + if component.kind == ComponentKind::RenderableModel { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gcg", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .streaming_model_header + .write_options( + &mut out, + binrw::Endian::Big, + &GCGHeaderArgs { + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } + if component.kind == ComponentKind::CollisionModel { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gol", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .collision_model + .write_options( + &mut out, + binrw::Endian::Big, + &CollisionModelArgs { + ror: true, + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(streaming_texture) => { @@ -243,7 +235,7 @@ fn process_component_wii( } } -fn process_component( +fn process_component_xbox( soup: &SoiSoup, section_id: u32, component: ComponentData, @@ -260,51 +252,45 @@ fn process_component( out.write_all(&component.data).unwrap(); } - // if component.kind == ComponentKind::RenderableModel { - // let header = soup - // .find_model(section_id, component.id, component.instance_id) - // .unwrap(); - // - // let path = PathBuf::from(format!( - // ".\\data\\CR_03\\{}.xng", - // component.path - // )); - // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - // let mut out = std::fs::File::create(path).unwrap(); - // header - // .streaming_model_header - // .write_options( - // &mut out, - // binrw::Endian::Big, - // &XNGHeaderArgs { - // streaming_data: component.data.clone(), - // }, - // ) - // .unwrap(); - // } - // if component.kind == ComponentKind::CollisionModel { - // let header = soup - // .find_collision_model(section_id, component.id, component.instance_id) - // .unwrap(); - // - // let path = PathBuf::from(format!( - // ".\\data\\CR_03\\{}.gol", - // component.path - // )); - // std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - // let mut out = std::fs::File::create(path).unwrap(); - // header - // .collision_model - // .write_options( - // &mut out, - // binrw::Endian::Big, - // &CollisionModelArgs { - // ror: false, - // streaming_data: component.data.clone(), - // }, - // ) - // .unwrap(); - // } + if component.kind == ComponentKind::RenderableModel { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.xng", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .streaming_model_header + .write_options( + &mut out, + binrw::Endian::Big, + &XNGHeaderArgs { + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } + if component.kind == ComponentKind::CollisionModel { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gol", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .collision_model + .write_options( + &mut out, + binrw::Endian::Big, + &CollisionModelArgs { + ror: false, + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(header) => { diff --git a/src/textures/gct.rs b/src/textures/gct.rs index 00e5262..4fc2676 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -1,5 +1,7 @@ use binrw::*; +use crate::div_round_up; + #[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] #[brw(repr = u32)] pub(crate) enum GCTFormat { @@ -11,14 +13,6 @@ pub(crate) enum GCTFormat { I8 = 0x3C, } -fn round_up(numToRound: usize, roundTo: usize) -> usize { - return ((numToRound + roundTo - 1) / roundTo) * roundTo; -} - -fn div_round_up(numToRound: usize, roundTo: usize) -> usize { - return (numToRound + roundTo - 1) / roundTo; -} - impl GCTFormat { pub fn get_block_dim(&self) -> (usize, usize) { match self { diff --git a/src/utils.rs b/src/utils.rs index d9d50b4..f7679e1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -63,6 +63,14 @@ pub struct Vector4i16 { pub w: i16, } +pub(crate) fn round_up(numToRound: usize, roundTo: usize) -> usize { + return ((numToRound + roundTo - 1) / roundTo) * roundTo; +} + +pub(crate) fn div_round_up(numToRound: usize, roundTo: usize) -> usize { + return (numToRound + roundTo - 1) / roundTo; +} + pub(crate) fn clean_path(input: &[u8]) -> String { let mut output = String::new(); From e41041c77ef7dd748955d19526d91083cc3d6801 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 15 Jun 2023 02:17:59 -0400 Subject: [PATCH 16/23] fix gcg writing --- src/models/gcg.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/models/gcg.rs b/src/models/gcg.rs index 1478050..ec395d8 100644 --- a/src/models/gcg.rs +++ b/src/models/gcg.rs @@ -141,7 +141,9 @@ impl BinWrite for GCGHeader { u8::write_options(&self.num_lod, writer, endian, ())?; u8::write_options(&self.skin_animates_flag, writer, endian, ())?; u8::write_options(&self.has_weight, writer, endian, ())?; + u8::write_options(&self.unused, writer, endian, ())?; if self.has_weight != 0 { + u16::write_options(&self.weight_count, writer, endian, ())?; Vec::::write_options(&self.weights.clone().unwrap(), writer, endian, ())?; } @@ -155,7 +157,8 @@ impl BinWrite for GCGHeader { u8::write_options(&(mesh.vertex_type - 0x80), writer, endian, ())?; GXAttrType::write_options(&mesh.vertex_attr_type, writer, endian, ())?; GXCompType::write_options(&mesh.vertex_data_type, writer, endian, ())?; - + u8::write_options(&mesh.xyz_frac_bits, writer, endian, ())?; + if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { GXAttrType::write_options(&mesh.normal_attr_type.unwrap(), writer, endian, ())?; GXCompType::write_options(&mesh.normal_data_type.unwrap(), writer, endian, ())?; @@ -226,10 +229,9 @@ impl BinWrite for GCGHeader { } // write the vertex block (positions, normals or vertex colors, uvs) - let vertex_block_end_offset = crate::round_up(offset_in_data + vertex_block_size, 32); - let vertex_block = (&args.streaming_data[offset_in_data..vertex_block_end_offset]).to_vec(); + let vertex_block = (&args.streaming_data[offset_in_data..offset_in_data + vertex_block_size]).to_vec(); Vec::::write_options(&vertex_block, writer, endian, ())?; - offset_in_data = vertex_block_end_offset; + offset_in_data = crate::round_up(offset_in_data + vertex_block_size, 32); let face_chunk: Vec = (&args.streaming_data [offset_in_data..offset_in_data + mesh.face_chunk_size as usize]) From e3d44e46bf71f0910ea1f76520087e87ca60afcd Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 15 Jun 2023 11:32:42 -0400 Subject: [PATCH 17/23] fix warnings, expose texture structs + utils --- src/collision.rs | 6 ++--- src/models/gcg.rs | 62 ++++++++++++++++++++++----------------------- src/models/mod.rs | 6 ++--- src/str.rs | 8 +++--- src/test.rs | 2 +- src/textures/gct.rs | 22 ++++++++-------- src/utils.rs | 8 +++--- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index 307a903..6d01949 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -259,7 +259,7 @@ impl std::fmt::Display for StreamingCollisionModel { self.model_info.look_vector, self.model_info.up_vector, self.model_info.zone - ); + )?; } else { write!( f, @@ -268,10 +268,10 @@ impl std::fmt::Display for StreamingCollisionModel { self.model_info.position, self.model_info.look_vector, self.model_info.up_vector, - ); + )?; } Ok(for param in self.parameters.iter() { - write!(f, "{}\n", param); + write!(f, "{}\n", param)?; }) } } diff --git a/src/models/gcg.rs b/src/models/gcg.rs index ec395d8..d5b3dd3 100644 --- a/src/models/gcg.rs +++ b/src/models/gcg.rs @@ -1,4 +1,3 @@ -use core::num; use std::io::{Seek, Write}; use binrw::{BinRead, BinResult, BinWrite, Endian}; @@ -8,21 +7,21 @@ use crate::{Bone, MeshName}; #[derive(BinRead, BinWrite, PartialEq, Debug, Clone, Copy)] #[brw(repr = u8)] pub enum GXCompType { - GX_U8 = 0, - GX_S8 = 1, - GX_U16 = 2, - GX_S16 = 3, - GX_F32 = 4, - GX_U32 = 5, + GxU8 = 0, + GxS8 = 1, + GxU16 = 2, + GxS16 = 3, + GxF32 = 4, + GxU32 = 5, } #[derive(BinRead, BinWrite, PartialEq, Debug, Clone, Copy)] #[brw(repr = u8)] pub enum GXAttrType { - GX_NONE = 0, - GX_DIRECT = 1, - GX_INDEX8 = 2, - GX_INDEX16 = 3, + GxNone = 0, + GxDirect = 1, + GxIndex8 = 2, + GxIndex16 = 3, } #[derive(BinRead, BinWrite, Debug)] @@ -158,7 +157,7 @@ impl BinWrite for GCGHeader { GXAttrType::write_options(&mesh.vertex_attr_type, writer, endian, ())?; GXCompType::write_options(&mesh.vertex_data_type, writer, endian, ())?; u8::write_options(&mesh.xyz_frac_bits, writer, endian, ())?; - + if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { GXAttrType::write_options(&mesh.normal_attr_type.unwrap(), writer, endian, ())?; GXCompType::write_options(&mesh.normal_data_type.unwrap(), writer, endian, ())?; @@ -193,22 +192,22 @@ impl BinWrite for GCGHeader { let mut vertex_block_size = 0; vertex_block_size += match mesh.vertex_data_type { - GXCompType::GX_U8 => mesh.vertex_count as usize * 3, - GXCompType::GX_S8 => mesh.vertex_count as usize * 3, - GXCompType::GX_U16 => mesh.vertex_count as usize * 6, - GXCompType::GX_S16 => mesh.vertex_count as usize * 6, - GXCompType::GX_F32 => mesh.vertex_count as usize * 12, - GXCompType::GX_U32 => mesh.vertex_count as usize * 12, + GXCompType::GxU8 => mesh.vertex_count as usize * 3, + GXCompType::GxS8 => mesh.vertex_count as usize * 3, + GXCompType::GxU16 => mesh.vertex_count as usize * 6, + GXCompType::GxS16 => mesh.vertex_count as usize * 6, + GXCompType::GxF32 => mesh.vertex_count as usize * 12, + GXCompType::GxU32 => mesh.vertex_count as usize * 12, }; if (mesh.vertex_type & 0x1) == 0x1 && (mesh.vertex_type & 0x8) == 0x8 { vertex_block_size += match mesh.normal_data_type { - Some(GXCompType::GX_U8) => mesh.normal_count as usize * 3, - Some(GXCompType::GX_S8) => mesh.normal_count as usize * 3, - Some(GXCompType::GX_U16) => mesh.normal_count as usize * 6, - Some(GXCompType::GX_S16) => mesh.normal_count as usize * 6, - Some(GXCompType::GX_F32) => mesh.normal_count as usize * 12, - Some(GXCompType::GX_U32) => mesh.normal_count as usize * 12, + Some(GXCompType::GxU8) => mesh.normal_count as usize * 3, + Some(GXCompType::GxS8) => mesh.normal_count as usize * 3, + Some(GXCompType::GxU16) => mesh.normal_count as usize * 6, + Some(GXCompType::GxS16) => mesh.normal_count as usize * 6, + Some(GXCompType::GxF32) => mesh.normal_count as usize * 12, + Some(GXCompType::GxU32) => mesh.normal_count as usize * 12, None => 0, }; } @@ -219,17 +218,18 @@ impl BinWrite for GCGHeader { if (mesh.vertex_type & 0x2) == 0x2 { vertex_block_size += match mesh.uv_data_type { - GXCompType::GX_U8 => mesh.uv_count as usize * 2, - GXCompType::GX_S8 => mesh.uv_count as usize * 2, - GXCompType::GX_U16 => mesh.uv_count as usize * 4, - GXCompType::GX_S16 => mesh.uv_count as usize * 4, - GXCompType::GX_F32 => mesh.uv_count as usize * 8, - GXCompType::GX_U32 => mesh.uv_count as usize * 8, + GXCompType::GxU8 => mesh.uv_count as usize * 2, + GXCompType::GxS8 => mesh.uv_count as usize * 2, + GXCompType::GxU16 => mesh.uv_count as usize * 4, + GXCompType::GxS16 => mesh.uv_count as usize * 4, + GXCompType::GxF32 => mesh.uv_count as usize * 8, + GXCompType::GxU32 => mesh.uv_count as usize * 8, }; } // write the vertex block (positions, normals or vertex colors, uvs) - let vertex_block = (&args.streaming_data[offset_in_data..offset_in_data + vertex_block_size]).to_vec(); + let vertex_block = + (&args.streaming_data[offset_in_data..offset_in_data + vertex_block_size]).to_vec(); Vec::::write_options(&vertex_block, writer, endian, ())?; offset_in_data = crate::round_up(offset_in_data + vertex_block_size, 32); diff --git a/src/models/mod.rs b/src/models/mod.rs index e75392c..eda60b4 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -29,7 +29,7 @@ impl = ()>> std::fmt::Display for StreamingRenderableM self.model_info.look_vector, self.model_info.up_vector, self.model_info.zone - ); + )?; } else { write!( f, @@ -38,10 +38,10 @@ impl = ()>> std::fmt::Display for StreamingRenderableM self.model_info.position, self.model_info.look_vector, self.model_info.up_vector, - ); + )?; } Ok(for param in self.parameters.iter() { - write!(f, "{}\n", param); + write!(f, "{}\n", param)?; }) } } diff --git a/src/str.rs b/src/str.rs index 39412ad..f077dc2 100644 --- a/src/str.rs +++ b/src/str.rs @@ -59,11 +59,11 @@ impl Str { Ok(SectionData { uncached, cached }) } else { let mut uncached_data = vec![0u8; header.uncached_data_size as usize]; - self.file.read(&mut uncached_data); + self.file.read(&mut uncached_data)?; let uncached = extract_components(§ion.uncached_components, uncached_data); let mut cached_data = vec![0u8; header.cached_data_size as usize]; - self.file.read(&mut cached_data); + self.file.read(&mut cached_data)?; let cached = extract_components(§ion.cached_components, cached_data); Ok(SectionData { uncached, cached }) @@ -73,11 +73,11 @@ impl Str { self.file.seek(SeekFrom::Start(section_offset))?; let mut uncached_data = vec![0u8; header.uncached_data_size as usize]; - self.file.read(&mut uncached_data); + self.file.read(&mut uncached_data)?; let uncached = extract_components(§ion.uncached_components, uncached_data); let mut cached_data = vec![0u8; header.cached_data_size as usize]; - self.file.read(&mut cached_data); + self.file.read(&mut cached_data)?; let cached = extract_components(§ion.cached_components, cached_data); Ok(SectionData { uncached, cached }) diff --git a/src/test.rs b/src/test.rs index 0dfe7a8..714aced 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,7 +6,7 @@ use x_flipper_360::*; use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; -use crate::{utils::*, GCGHeader, GCGHeaderArgs, XNGHeader}; +use crate::{GCGHeader, GCGHeaderArgs, XNGHeader}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] diff --git a/src/textures/gct.rs b/src/textures/gct.rs index 4fc2676..e5cfc33 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -4,12 +4,12 @@ use crate::div_round_up; #[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] #[brw(repr = u32)] -pub(crate) enum GCTFormat { +pub enum GCTFormat { Rgba8 = 0x0F, Cmpr = 0x29, - Cmpr_MM = 0x2A, + CmprMm = 0x2A, Ci8 = 0x3A, - Ci8_MM = 0x3B, + Ci8Mm = 0x3B, I8 = 0x3C, } @@ -18,9 +18,9 @@ impl GCTFormat { match self { GCTFormat::Rgba8 => (4, 4), GCTFormat::Cmpr => (8, 8), - GCTFormat::Cmpr_MM => (8, 8), + GCTFormat::CmprMm => (8, 8), GCTFormat::Ci8 => (8, 4), - GCTFormat::Ci8_MM => (8, 4), + GCTFormat::Ci8Mm => (8, 4), GCTFormat::I8 => (8, 4), } } @@ -28,9 +28,9 @@ impl GCTFormat { match self { GCTFormat::Rgba8 => 32, GCTFormat::Cmpr => 4, - GCTFormat::Cmpr_MM => 4, + GCTFormat::CmprMm => 4, GCTFormat::Ci8 => 8, - GCTFormat::Ci8_MM => 8, + GCTFormat::Ci8Mm => 8, GCTFormat::I8 => 8, } } @@ -50,15 +50,15 @@ impl std::fmt::Display for GCTFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { GCTFormat::Rgba8 => write!(f, "Rgba8"), - GCTFormat::Cmpr | GCTFormat::Cmpr_MM => write!(f, "Cmpr"), - GCTFormat::Ci8 | GCTFormat::Ci8_MM => write!(f, "Ci8"), + GCTFormat::Cmpr | GCTFormat::CmprMm => write!(f, "Cmpr"), + GCTFormat::Ci8 | GCTFormat::Ci8Mm => write!(f, "Ci8"), GCTFormat::I8 => write!(f, "I8"), } } } #[derive(BinRead, BinWrite, Debug, Clone)] -pub(crate) struct GCNTextureHeader { +pub struct GCNTextureHeader { pub version: u32, pub format: GCTFormat, pub palette_size: u32, @@ -72,7 +72,7 @@ pub(crate) struct GCNTextureHeader { } #[derive(BinRead, BinWrite, Debug)] -pub(crate) struct GCTSurfaceHeader { +pub struct GCTSurfaceHeader { pub width: u32, pub height: u32, pub size: u32, diff --git a/src/utils.rs b/src/utils.rs index f7679e1..38cfa68 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -63,12 +63,12 @@ pub struct Vector4i16 { pub w: i16, } -pub(crate) fn round_up(numToRound: usize, roundTo: usize) -> usize { - return ((numToRound + roundTo - 1) / roundTo) * roundTo; +pub fn round_up(num_to_round: usize, round_to: usize) -> usize { + return ((num_to_round + round_to - 1) / round_to) * round_to; } -pub(crate) fn div_round_up(numToRound: usize, roundTo: usize) -> usize { - return (numToRound + roundTo - 1) / roundTo; +pub fn div_round_up(num_to_round: usize, round_to: usize) -> usize { + return (num_to_round + round_to - 1) / round_to; } pub(crate) fn clean_path(input: &[u8]) -> String { From 0d18c2009c75af35a159cf2ff9a85e6eeadebe6a Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 15 Jun 2023 11:35:18 -0400 Subject: [PATCH 18/23] expose clean_path --- src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.rs b/src/utils.rs index 38cfa68..1e26795 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -71,7 +71,7 @@ pub fn div_round_up(num_to_round: usize, round_to: usize) -> usize { return (num_to_round + round_to - 1) / round_to; } -pub(crate) fn clean_path(input: &[u8]) -> String { +pub fn clean_path(input: &[u8]) -> String { let mut output = String::new(); for c in input { From b2d208bd1cc9eb6e7508e161e84b501944425341 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:03:57 -0400 Subject: [PATCH 19/23] fix extracting on xbox --- src/models/gcg.rs | 2 +- src/soi.rs | 3 ++- src/test.rs | 7 +++---- src/textures/gct.rs | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/models/gcg.rs b/src/models/gcg.rs index d5b3dd3..8990b17 100644 --- a/src/models/gcg.rs +++ b/src/models/gcg.rs @@ -239,7 +239,7 @@ impl BinWrite for GCGHeader { Vec::::write_options(&face_chunk, writer, endian, ())?; offset_in_data += mesh.face_chunk_size as usize; } - println!("{}, {}", offset_in_data, args.streaming_data.len()); + assert_eq!(offset_in_data, args.streaming_data.len()); } Ok(()) } diff --git a/src/soi.rs b/src/soi.rs index a890069..6923f9f 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -77,7 +77,8 @@ impl std::fmt::Display for StreamingParameter { #[derive(BinRead, Debug)] pub struct StreamingTexture = ()> + 'static> { pub model_info: ModelInfo, - //pub padding: u32, + // padding on xbox, version on wii + pub version: u32, pub header: TH, } diff --git a/src/test.rs b/src/test.rs index 714aced..0df3cc1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -177,10 +177,9 @@ fn process_component_wii( std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - // Clone the header so we can set the version back to 2 (streamed GCTs have a flag OR'd on the version) - let mut gct_file_header = streaming_texture.header.clone(); - gct_file_header.version = 2; - gct_file_header.write_be(&mut out).unwrap(); + // Write the version out (streamed GCTs have a flag OR'd on the version, so we set it manually to 2 [otherwise we would use streaming_texture.version]) + u32::write_options(&2, &mut out, binrw::Endian::Big, ()).unwrap(); + streaming_texture.header.write_be(&mut out).unwrap(); // Keeps track of our position in the streaming data. let mut offset = 0; diff --git a/src/textures/gct.rs b/src/textures/gct.rs index e5cfc33..8e8c85c 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -59,7 +59,6 @@ impl std::fmt::Display for GCTFormat { #[derive(BinRead, BinWrite, Debug, Clone)] pub struct GCNTextureHeader { - pub version: u32, pub format: GCTFormat, pub palette_size: u32, From d708dd53b6590d930f1eef6cf44012322df4445f Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:22:36 -0400 Subject: [PATCH 20/23] make static texture headers generic --- src/soi.rs | 26 ++++++++++++-------------- src/soi_soup.rs | 15 ++++++++------- src/test.rs | 17 +++++++++-------- src/textures/dds.rs | 8 ++++++++ src/textures/gct.rs | 25 +++++++++++++++++++++++++ src/textures/mod.rs | 2 ++ 6 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 src/textures/dds.rs diff --git a/src/soi.rs b/src/soi.rs index 6923f9f..07cad0b 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -75,24 +75,22 @@ impl std::fmt::Display for StreamingParameter { } #[derive(BinRead, Debug)] -pub struct StreamingTexture = ()> + 'static> { +pub struct StreamingTexture = ()> + 'static> { pub model_info: ModelInfo, // padding on xbox, version on wii pub version: u32, - pub header: TH, + pub header: StreamingTH, } #[derive(BinRead, Debug)] -pub struct StaticTexture { +pub struct StaticTexture = ()> + 'static> { pub model_info: ModelInfo, - pub dds_size: u32, - #[br(count = dds_size)] - pub header_file: Vec, + pub static_texture_header: StaticTH, } #[derive(BinRead, Debug)] -pub struct Soi = ()> + 'static, MH: BinRead = ()> + 'static> +pub struct Soi = ()> + 'static, StaticTH: BinRead = ()> + 'static, MH: BinRead = ()> + 'static> { pub header: Header, @@ -103,10 +101,10 @@ pub struct Soi = ()> + 'static, MH: BinRead, #[br(count = header.streaming_textures)] - streaming_textures: Vec>, + streaming_textures: Vec>, #[br(count = header.static_textures)] - static_textures: Vec, + static_textures: Vec>, #[br(count = header.motion_packs)] motion_packs: Vec, @@ -119,7 +117,7 @@ pub struct Soi = ()> + 'static, MH: BinRead, } -impl = ()>, MH: BinRead = ()>> Soi { +impl = ()>, StaticTH: BinRead = ()>, MH: BinRead = ()>> Soi { pub fn read(path: &Path) -> BinResult { let mut file = File::open(path)?; Self::read_file(&mut file) @@ -129,11 +127,11 @@ impl = ()>, MH: BinRead = ()>> Soi &[StreamingTexture] { + pub fn get_streaming_textures(&self) -> &[StreamingTexture] { return &self.streaming_textures; } - pub fn get_static_textures(&self) -> &[StaticTexture] { + pub fn get_static_textures(&self) -> &[StaticTexture] { return &self.static_textures; } @@ -149,7 +147,7 @@ impl = ()>, MH: BinRead = ()>> Soi Option<&StaticTexture> { + pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { for texture in &self.static_textures { let model_info = &texture.model_info; if model_info.section_id == section_id as i32 @@ -166,7 +164,7 @@ impl = ()>, MH: BinRead = ()>> Soi Option<&StreamingTexture> { + ) -> Option<&StreamingTexture> { for texture in &self.streaming_textures { let model_info = &texture.model_info; if model_info.section_id == section_id as i32 diff --git a/src/soi_soup.rs b/src/soi_soup.rs index ff1681e..17f5228 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -8,14 +8,15 @@ use crate::{ }; pub struct SoiSoup< - TH: BinRead = ()> + 'static, + StreamingTH: BinRead = ()> + 'static, + StaticTH: BinRead = ()> + 'static, MH: BinRead = ()> + 'static, > { toc: Toc, - soi: Soi, + soi: Soi, } -impl = ()>, MH: BinRead = ()>> SoiSoup { +impl = ()>, StaticTH: BinRead = ()>, MH: BinRead = ()>> SoiSoup { pub fn cook(toc_path: &Path, soi_path: &Path) -> BinResult { let soi = Soi::read(soi_path)?; let toc = Toc::read(toc_path, soi.header.version == 0x101)?; @@ -45,11 +46,11 @@ impl = ()>, MH: BinRead = ()>> SoiSoup &[StreamingTexture] { + pub fn streaming_textures(&self) -> &[StreamingTexture] { self.soi.get_streaming_textures() } - pub fn static_textures(&self) -> &[StaticTexture] { + pub fn static_textures(&self) -> &[StaticTexture] { self.soi.get_static_textures() } @@ -80,7 +81,7 @@ impl = ()>, MH: BinRead = ()>> SoiSoup Option<&StaticTexture> { + ) -> Option<&StaticTexture> { if let Some(header) = self.soi.find_static_texture(section_id, component_id) { return Some(header); } @@ -94,7 +95,7 @@ impl = ()>, MH: BinRead = ()>> SoiSoup Option<&StreamingTexture> { + ) -> Option<&StreamingTexture> { if let Some(header) = self.soi.find_streaming_texture(section_id, component_id) { return Some(header); } diff --git a/src/test.rs b/src/test.rs index 0df3cc1..2b6ccca 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,7 +6,7 @@ use x_flipper_360::*; use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; -use crate::{GCGHeader, GCGHeaderArgs, XNGHeader}; +use crate::{GCGHeader, GCGHeaderArgs, XNGHeader, X360StaticTextureHeader, GCNStaticTextureHeader}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; #[test] @@ -15,7 +15,7 @@ fn extract() { let soi_path = Path::new("./data/CR_03.gcn.soi"); let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); for (id, section) in soup.find_sections().iter().enumerate() { @@ -36,7 +36,7 @@ fn dump_scn() { let toc_path = Path::new("./data/CR_03.gcn.toc"); let soi_path = Path::new("./data/CR_03.gcn.soi"); let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; let mut num_static_models = 1; @@ -69,10 +69,11 @@ fn dump_scn() { } fn print_component< - TH: binrw::BinRead = ()> + 'static, + StreamingTH: binrw::BinRead = ()> + 'static, + StaticTH: binrw::BinRead = ()> + 'static, MH: binrw::BinRead = ()> + 'static, >( - soup: &SoiSoup, + soup: &SoiSoup, section_id: u32, component: ComponentData, num_anim_models: &mut i32, @@ -115,7 +116,7 @@ fn print_component< } fn process_component_wii( - soup: &SoiSoup, + soup: &SoiSoup, section_id: u32, component: ComponentData, ) { @@ -235,7 +236,7 @@ fn process_component_wii( } fn process_component_xbox( - soup: &SoiSoup, + soup: &SoiSoup, section_id: u32, component: ComponentData, ) { @@ -391,7 +392,7 @@ fn process_component_xbox( let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - out.write_all(&static_texture.header_file).unwrap(); + out.write_all(&static_texture.static_texture_header.header_file).unwrap(); } None => panic!("Failed to find texture header."), }, diff --git a/src/textures/dds.rs b/src/textures/dds.rs new file mode 100644 index 0000000..2cf3771 --- /dev/null +++ b/src/textures/dds.rs @@ -0,0 +1,8 @@ +use binrw::BinRead; + +#[derive(BinRead, Debug)] +pub struct X360StaticTextureHeader { + pub dds_size: u32, + #[br(count = dds_size)] + pub header_file: Vec, +} \ No newline at end of file diff --git a/src/textures/gct.rs b/src/textures/gct.rs index 8e8c85c..342d8be 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -98,3 +98,28 @@ impl GCNTextureHeader { size_bytes } } + +#[derive(BinRead, BinWrite)] +pub struct GCTSurface { + pub header: GCTSurfaceHeader, + + #[br(count = header.size)] + pub data: Vec, +} + +#[derive(BinRead, BinWrite)] +pub struct GCNStaticTextureHeader { + pub version: u32, + pub format: GCTFormat, + pub palette_size: u32, + + #[br(count = palette_size * 2)] + pub palette: Vec, + + pub mip_count: u32, + pub width: u32, + pub height: u32, + + #[br(count = mip_count)] + pub mips: Vec +} \ No newline at end of file diff --git a/src/textures/mod.rs b/src/textures/mod.rs index 1741c64..7874a72 100644 --- a/src/textures/mod.rs +++ b/src/textures/mod.rs @@ -1,2 +1,4 @@ mod gct; +mod dds; pub use self::gct::*; +pub use self::dds::*; \ No newline at end of file From b00f1a4b27502d74536a441fa8cab3aea3a80f32 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Tue, 25 Mar 2025 22:49:12 -0500 Subject: [PATCH 21/23] i have no fucking clue what i changed --- src/collision.rs | 55 ++++----- src/lib.rs | 2 + src/models/dxg.rs | 283 ++++++++++++++++++++++++++++++++++++++++++++ src/models/mod.rs | 4 +- src/motion.rs | 4 - src/res.rs | 99 ++++++++++++++++ src/soi.rs | 29 +++-- src/soi_soup.rs | 15 ++- src/test.rs | 224 +++++++++++++++++++++++++++++------ src/textures/dds.rs | 2 +- src/textures/dxt.rs | 140 ++++++++++++++++++++++ src/textures/gct.rs | 4 +- src/textures/mod.rs | 6 +- src/toc.rs | 20 ++++ src/utils.rs | 2 - 15 files changed, 792 insertions(+), 97 deletions(-) create mode 100644 src/models/dxg.rs create mode 100644 src/res.rs create mode 100644 src/textures/dxt.rs diff --git a/src/collision.rs b/src/collision.rs index 6d01949..0cff265 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -43,7 +43,6 @@ impl std::fmt::Display for CollisionType { } #[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] struct TreeFace { volume: f32, vectors: [Vector3; 2], @@ -51,7 +50,6 @@ struct TreeFace { } #[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] struct RaceORamaStreamingDataTreeFace { vectors: [Vector4; 2], type_indices: [i16; 2], @@ -82,7 +80,6 @@ impl RaceORamaStreamingDataTreeFace { } #[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] struct StreamingDataTreeFace { volume: f32, radius: f32, @@ -101,7 +98,6 @@ impl StreamingDataTreeFace { } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] struct TreeFaceLeaf { dvalue: f32, vector: Vector3, @@ -109,7 +105,6 @@ struct TreeFaceLeaf { } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] struct RaceORamaStreamingDataTreeFaceLeaf { vector: Vector4, dvalue: f32, @@ -132,7 +127,6 @@ impl RaceORamaStreamingDataTreeFaceLeaf { } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] struct StreamingDataTreeFaceLeaf { vertices: [i16; 3], unknown1: f32, @@ -164,7 +158,6 @@ impl StreamingDataTreeFaceLeaf { } #[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] struct SoultreeCollisionObject { temp_cmt: i32, @@ -196,7 +189,6 @@ struct SoultreeCollisionObject { } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] struct FinitePlaneStruct { local_vertex_bl: Vector3, local_vertex_br: Vector3, @@ -206,16 +198,14 @@ struct FinitePlaneStruct { } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] struct StreamingHeirarchyEntry { object_id: i32, object: SoultreeCollisionObject, } #[derive(BinRead, Debug)] -#[brw(big)] -#[br(magic = b"\x00\x00\x04\xD2")] pub struct CollisionModel { + magic: u32, col_type: [u8; 4], version: i32, collision_type: CollisionType, @@ -294,8 +284,7 @@ impl BinWrite for CollisionModel { endian: Endian, args: Self::Args<'_>, ) -> BinResult<()> { - let magic = b"\x00\x00\x04\xD2".to_vec(); - Vec::::write_options(&magic, writer, endian, ())?; + u32::write_options(&1234, writer, endian, ())?; self.col_type.write_options(writer, endian, ())?; i32::write_options(&self.version, writer, endian, ())?; @@ -333,7 +322,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -355,7 +344,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); global_vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -376,7 +365,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -398,7 +387,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let quantized_vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -429,7 +418,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -461,7 +450,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.vertex_count as usize) .finalize(), @@ -497,7 +486,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_tree_faces = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.tree_face_count as usize) .finalize(), @@ -516,7 +505,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let mn_tree_faces = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.tree_face_count as usize) .finalize(), @@ -536,7 +525,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_face_leaves = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.tree_face_leaf_count as usize) .finalize(), @@ -555,7 +544,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let mn_face_leaves = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(self.object.tree_face_leaf_count as usize) .finalize(), @@ -595,7 +584,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -617,7 +606,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); global_vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -638,7 +627,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -660,7 +649,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let quantized_vertices = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -691,7 +680,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -723,7 +712,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let normals = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.vertex_count as usize) .finalize(), @@ -759,7 +748,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_tree_faces = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.tree_face_count as usize) .finalize(), @@ -778,7 +767,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let mn_tree_faces = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.tree_face_count as usize) .finalize(), @@ -798,7 +787,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let ror_face_leaves = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.tree_face_leaf_count as usize) .finalize(), @@ -817,7 +806,7 @@ impl BinWrite for CollisionModel { cursor.set_position(offset_in_data as u64); let mn_face_leaves = Vec::::read_options( &mut cursor, - binrw::Endian::Big, + endian, binrw::VecArgs::builder() .count(object.object.tree_face_leaf_count as usize) .finalize(), diff --git a/src/lib.rs b/src/lib.rs index bb34d05..4965899 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub use crate::collision::*; pub use crate::models::*; pub use crate::motion::*; +pub use crate::res::*; pub use crate::soi::*; pub use crate::soi_soup::*; pub use crate::str::*; @@ -11,6 +12,7 @@ pub use crate::utils::*; mod collision; mod models; mod motion; +mod res; mod soi; mod soi_soup; mod str; diff --git a/src/models/dxg.rs b/src/models/dxg.rs new file mode 100644 index 0000000..7521930 --- /dev/null +++ b/src/models/dxg.rs @@ -0,0 +1,283 @@ +use binrw::{BinRead, BinResult, BinWrite, Endian}; +use std::io::{Seek, Write}; + +use crate::{utils::*, Bone, MeshName}; + +// https://github.com/leeao/carsraceorama/blob/master/carsraceorama/CarsTypes.h#L43 +const D3DVSDT_FLOAT1: u8 = 0x12; // 1D float expanded to (value, 0., 0., 1.) +const D3DVSDT_FLOAT2: u8 = 0x22; // 2D float expanded to (value, value, 0., 1.) +const D3DVSDT_FLOAT3: u8 = 0x32; // 3D float expanded to (value, value, value, 1.) +const D3DVSDT_FLOAT4: u8 = 0x42; // 4D float +const D3DVSDT_D3DCOLOR: u8 = 0x40; // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) +const D3DVSDT_SHORT2: u8 = 0x25; // 2D signed short expanded to (value, value, 0., 1.) +const D3DVSDT_SHORT4: u8 = 0x45; // 4D signed short + +// The following are Xbox extensions +const D3DVSDT_NORMSHORT1: u8 = 0x11; // 1D signed, normalized short expanded to (value, 0, 0., 1.) + +// (signed, normalized short maps from -1.0 to 1.0) +const D3DVSDT_NORMSHORT2: u8 = 0x21; // 2D signed, normalized short expanded to (value, value, 0., 1.) +const D3DVSDT_NORMSHORT3: u8 = 0x31; // 3D signed, normalized short expanded to (value, value, value, 1.) +const D3DVSDT_NORMSHORT4: u8 = 0x41; // 4D signed, normalized short expanded to (value, value, value, value) +const D3DVSDT_NORMPACKED3: u8 = 0x16; // 3 signed, normalized components packed in 32-bits. (11,11,10). + +// Each component ranges from -1.0 to 1.0. +// Expanded to (value, value, value, 1.) +const D3DVSDT_SHORT1: u8 = 0x15; // 1D signed short expanded to (value, 0., 0., 1.) + +// Signed shorts map to the range [-32768, 32767] +const D3DVSDT_SHORT3: u8 = 0x35; // 3D signed short expanded to (value, value, value, 1.) +const D3DVSDT_PBYTE1: u8 = 0x14; // 1D packed byte expanded to (value, 0., 0., 1.) + +// Packed bytes map to the range [0, 1] +const D3DVSDT_PBYTE2: u8 = 0x24; // 2D packed byte expanded to (value, value, 0., 1.) +const D3DVSDT_PBYTE3: u8 = 0x34; // 3D packed byte expanded to (value, value, value, 1.) +const D3DVSDT_PBYTE4: u8 = 0x44; // 4D packed byte expanded to (value, value, value, value) +const D3DVSDT_FLOAT2H: u8 = 0x72; // 2D homogeneous float expanded to (value, value,0., value.) + +// Useful for projective texture coordinates. +const D3DVSDT_NONE: u8 = 0x02; // No stream data + +const DEFAULT_VERTEX_FORMATS: [u8; 16] = [ + D3DVSDT_FLOAT3, + D3DVSDT_FLOAT3, + D3DVSDT_D3DCOLOR, + D3DVSDT_FLOAT2, + D3DVSDT_FLOAT1, + D3DVSDT_NONE, + D3DVSDT_NONE, + D3DVSDT_NONE, + D3DVSDT_FLOAT4, + D3DVSDT_FLOAT4, + D3DVSDT_FLOAT2, + D3DVSDT_NONE, + D3DVSDT_NONE, + D3DVSDT_NONE, + D3DVSDT_NONE, + D3DVSDT_NONE, +]; + +#[derive(Default, BinRead, BinWrite, Debug)] +#[brw(little)] +pub struct DXGDeltaBlock { + num_channels: u32, + + #[br(count = 64)] + controller_name: Vec, + + num_vertices: u32, + xyz_bits: u32, + force_unique: u8, + unk: u32, + unk2: u32, + + delta_count: u32, + + #[br(count = delta_count)] + delta_positions: Vec, + + #[br(count = delta_count)] + delta_normals: Vec, + + #[br(count = delta_count)] + delta_indices: Vec, + + #[br(count = num_vertices)] + positions: Vec, + + #[br(count = num_vertices)] + normals: Vec, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(little)] +pub struct StreamingDXGMesh { + pub surface_index: u32, + pub vertex_type: u32, + + #[br(if ((vertex_type & 0x2000) == 0x2000))] + pub compression_stuff: Option<[f32; 8]>, + + #[br(if ((vertex_type & 0x2000) == 0x2000))] + pub vertex_formats: Option<[u8; 16]>, + + pub num_texture_coordinate_sets: u8, + pub compressed: u8, + pub streaming: u8, + pub unk: u8, + pub unk2: u8, + pub unk3: u8, + + #[br(count = num_texture_coordinate_sets)] + pub texture_coordinate_sets: Vec, + + pub num_vertices: u16, + pub num_face_indices: u16, + + #[br(if ((vertex_type & 0x100) == 0x100))] + pub delta_block: Option, +} + +#[derive(BinRead, BinWrite, Debug)] +#[brw(little)] +pub struct DXGLod { + pub auto_lod_value: f32, + pub num_meshes: u32, + + #[br(count = num_meshes)] + pub meshes: Vec, +} + +#[derive(BinRead, Debug)] +#[brw(little)] +#[br(magic = b"dgs\0")] +pub struct DXGHeader { + version: i32, + num_bones: u32, + + #[br(count = num_bones)] + bones: Vec, + + num_mesh_names: i32, + + #[br(count = num_mesh_names)] + pub mesh_names: Vec, + + pub num_lod: u8, + skin_animates_flag: u8, + has_weight: u8, + unused: u8, + + #[br(count = num_lod)] + pub lods: Vec, +} + +// BinrwNamedArgs +#[derive(Clone, Debug)] +pub struct DXGHeaderArgs { + pub streaming_data: Vec, +} + +// This BinWrite implementation actually restructures the streaming component data plus the header data in the SOI to form a proper DXG file. +// As such, the streaming data must be passed to write_options. +impl BinWrite for DXGHeader { + type Args<'a> = &'a DXGHeaderArgs; + + fn write_options( + &self, + writer: &mut W, + endian: Endian, + args: Self::Args<'_>, + ) -> BinResult<()> { + let magic = b"dxg\0".to_vec(); + Vec::::write_options(&magic, writer, endian, ())?; + i32::write_options(&self.version, writer, endian, ())?; + u32::write_options(&self.num_bones, writer, endian, ())?; + Vec::::write_options(&self.bones, writer, endian, ())?; + i32::write_options(&self.num_mesh_names, writer, endian, ())?; + Vec::::write_options(&self.mesh_names, writer, endian, ())?; + u8::write_options(&self.num_lod, writer, endian, ())?; + u8::write_options(&self.skin_animates_flag, writer, endian, ())?; + u8::write_options(&self.has_weight, writer, endian, ())?; + u8::write_options(&self.unused, writer, endian, ())?; + + let mut offset_in_data: usize = 0; + + for lod in &self.lods { + f32::write_options(&lod.auto_lod_value, writer, endian, ())?; + u32::write_options(&lod.num_meshes, writer, endian, ())?; + for mesh in &lod.meshes { + u32::write_options(&mesh.surface_index, writer, endian, ())?; + u32::write_options(&mesh.vertex_type, writer, endian, ())?; + if let Some(compression_stuff) = mesh.compression_stuff { + compression_stuff.write_options(writer, endian, ())?; + } + + let vertex_formats = if let Some(vertex_formats) = mesh.vertex_formats { + vertex_formats + } else { + DEFAULT_VERTEX_FORMATS.clone() + }; + if mesh.vertex_formats.is_some() { + vertex_formats.write_options(writer, endian, ())?; + } + + u8::write_options(&mesh.num_texture_coordinate_sets, writer, endian, ())?; + u8::write_options(&mesh.compressed, writer, endian, ())?; + u8::write_options(&0, writer, endian, ())?; + u8::write_options(&mesh.unk, writer, endian, ())?; + u8::write_options(&mesh.unk2, writer, endian, ())?; + u8::write_options(&mesh.unk3, writer, endian, ())?; + Vec::::write_options(&mesh.texture_coordinate_sets, writer, endian, ())?; + + u16::write_options(&mesh.num_vertices, writer, endian, ())?; + u16::write_options(&mesh.num_face_indices, writer, endian, ())?; + + assert!(mesh.streaming == 1); + + let ty = mesh.vertex_type; + let mut offset: usize = mesh.num_face_indices as usize * 2; + + if (ty & 0x01) == 0x01 { + let attribute_size = if vertex_formats[0] == D3DVSDT_FLOAT3 { + mesh.num_vertices as usize * 12 + } else if vertex_formats[0] == D3DVSDT_SHORT3 { + mesh.num_vertices as usize * 6 + } else if vertex_formats[0] == D3DVSDT_PBYTE3 { + mesh.num_vertices as usize * 3 + } else { + 0 + }; + offset += attribute_size; + } + if (ty & 0x02) == 0x02 { + let attribute_size = if vertex_formats[1] == D3DVSDT_FLOAT3 { + mesh.num_vertices as usize * 12 + } else if vertex_formats[1] == D3DVSDT_SHORT3 { + mesh.num_vertices as usize * 6 + } else if vertex_formats[1] == D3DVSDT_PBYTE3 { + mesh.num_vertices as usize * 3 + } else { + 0 + }; + offset += attribute_size; + } + if (ty & 0x08) == 0x08 { + offset += mesh.num_vertices as usize * 4; + } + if (ty & 0x04) == 0x04 { + let attribute_size = if vertex_formats[3] == D3DVSDT_FLOAT2 { + mesh.num_vertices as usize * 8 + } else if vertex_formats[3] == D3DVSDT_SHORT2 { + mesh.num_vertices as usize * 4 + } else if vertex_formats[3] == D3DVSDT_PBYTE2 { + mesh.num_vertices as usize * 2 + } else { + 0 + }; + offset += attribute_size; + } + if (ty & 0x10) == 0x10 { + offset += mesh.num_vertices as usize * 8; + } + if (ty & 0x40) == 0x40 { + offset += mesh.num_vertices as usize * 4; + } + if (ty & 0x1000) == 0x1000 { + offset += mesh.num_vertices as usize * 32; + } + + let data = (&args.streaming_data[offset_in_data..offset_in_data + offset]).to_vec(); + Vec::::write_options(&data, writer, endian, ())?; + + offset_in_data += offset; + + if let Some(delta_block) = &mesh.delta_block { + delta_block.write_options(writer, endian, ())?; + } + } + } + Ok(()) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index eda60b4..6f3ffb8 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,4 @@ +mod dxg; mod gcg; mod xng; use binrw::BinRead; @@ -5,6 +6,7 @@ use binrw::BinWrite; use crate::clean_string; +pub use self::dxg::*; pub use self::gcg::*; pub use self::xng::*; @@ -47,14 +49,12 @@ impl = ()>> std::fmt::Display for StreamingRenderableM } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] pub struct MeshName { #[br(count = 64)] name: Vec, } #[derive(BinRead, BinWrite, Debug)] -#[brw(big)] pub struct Bone { name: [u8; 128], matrix: [f32; 16], diff --git a/src/motion.rs b/src/motion.rs index 5a2b343..add503e 100644 --- a/src/motion.rs +++ b/src/motion.rs @@ -1,8 +1,6 @@ use binrw::{BinRead, BinWrite}; #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] struct MotionPackString { len: u32, @@ -11,8 +9,6 @@ struct MotionPackString { } #[derive(BinRead, BinWrite, Debug)] -#[br(big)] -#[bw(big)] pub struct StreamingMotionPackHeader { version: i32, motion_type: i32, diff --git a/src/res.rs b/src/res.rs new file mode 100644 index 0000000..26787ff --- /dev/null +++ b/src/res.rs @@ -0,0 +1,99 @@ +use std::{collections::HashMap, fs::File, io::Read, path::Path}; + +use binrw::{BinRead, BinReaderExt, BinResult}; +use flate2::read::ZlibDecoder; + +use crate::clean_path; + +#[derive(BinRead)] +pub struct OffsetEntry { + pub name_len: u32, + + #[br(count = name_len)] + pub name: Vec, + + pub start_offset: u32, + pub size: u32, +} + +#[derive(BinRead)] +pub struct Header { + pub version: u32, + pub header_size: u32, + + #[br(if(version == 3))] + pub resource_file_type_flags: Option, + + pub user_data_type: u32, + + /// Size in bytes + pub sector_list_size: u32, + + #[br(count = sector_list_size / 2)] + pub sector_list: Vec, + + #[br(count = header_size - (8 + sector_list_size + (resource_file_type_flags.is_some() as u32 * 4)))] + pub user_data: Vec, + + /// Actual length + pub offset_table_len: u32, + /// Size in bytes + pub offset_table_size: u32, + + #[br(count = offset_table_len)] + pub offset_table: Vec, +} + +pub struct Res { + res_file_header: Header, + files: HashMap>, +} + +impl Res { + pub fn read(path: &Path) -> BinResult { + let mut file = File::open(path)?; + Self::read_file(&mut file) + } + + pub fn read_file(file: &mut File) -> BinResult { + let res_file_header: Header = file.read_le()?; + let mut files = HashMap::new(); + let mut compressed_data = Vec::new(); + file.read_to_end(&mut compressed_data).unwrap(); + + let mut current_offset = 0; + while compressed_data[current_offset] == 0 { + current_offset += 1; + } + + for offset_entry in res_file_header.offset_table.iter() { + let path = clean_path(&offset_entry.name); + + let mut decompressed_data = Vec::new(); + let mut decoder = ZlibDecoder::new(&compressed_data[current_offset..]); + decoder.read_to_end(&mut decompressed_data)?; + + // advance the offset in the compressed data buffer by the size that the decoder read while decompressing this specific file. + current_offset += decoder.total_in() as usize; + + // a dummy 0xFF byte is sometimes placed so the game knows when a file has been read, so we check for it and advance the offset accordingly. + if compressed_data[current_offset] == 0xFF { + current_offset += 1; + } + + files.insert(path, decompressed_data); + } + Ok(Self { + res_file_header, + files, + }) + } + + pub fn get_file(&self, path: String) -> Option<&[u8]> { + if self.files.contains_key(&path) { + Some(&self.files[&path]) + } else { + None + } + } +} diff --git a/src/soi.rs b/src/soi.rs index 07cad0b..c8b00f8 100644 --- a/src/soi.rs +++ b/src/soi.rs @@ -1,6 +1,7 @@ use std::fs::File; use std::path::Path; +use binrw::Endian; use binrw::{BinRead, BinReaderExt, BinResult}; use crate::collision::*; @@ -90,8 +91,11 @@ pub struct StaticTexture = ()> + 'static> { } #[derive(BinRead, Debug)] -pub struct Soi = ()> + 'static, StaticTH: BinRead = ()> + 'static, MH: BinRead = ()> + 'static> -{ +pub struct Soi< + StreamingTH: BinRead = ()> + 'static, + StaticTH: BinRead = ()> + 'static, + MH: BinRead = ()> + 'static, +> { pub header: Header, #[br(count = header.uncached_pages)] @@ -117,14 +121,19 @@ pub struct Soi = ()> + 'static, StaticTH: Bin collision_models: Vec, } -impl = ()>, StaticTH: BinRead = ()>, MH: BinRead = ()>> Soi { - pub fn read(path: &Path) -> BinResult { +impl< + StreamingTH: BinRead = ()>, + StaticTH: BinRead = ()>, + MH: BinRead = ()>, + > Soi +{ + pub fn read(path: &Path, endian: Endian) -> BinResult { let mut file = File::open(path)?; - Self::read_file(&mut file) + Self::read_file(&mut file, endian) } - pub fn read_file(file: &mut File) -> BinResult { - file.read_be() + pub fn read_file(file: &mut File, endian: Endian) -> BinResult { + file.read_type(endian) } pub fn get_streaming_textures(&self) -> &[StreamingTexture] { @@ -147,7 +156,11 @@ impl = ()>, StaticTH: BinRead = return &self.collision_models; } - pub fn find_static_texture(&self, section_id: u32, component_id: u32) -> Option<&StaticTexture> { + pub fn find_static_texture( + &self, + section_id: u32, + component_id: u32, + ) -> Option<&StaticTexture> { for texture in &self.static_textures { let model_info = &texture.model_info; if model_info.section_id == section_id as i32 diff --git a/src/soi_soup.rs b/src/soi_soup.rs index 17f5228..c129792 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -1,10 +1,10 @@ use std::path::Path; -use binrw::{BinRead, BinResult}; +use binrw::{BinRead, BinResult, Endian}; use crate::{ ComponentHeader, Section, Soi, StaticTexture, StreamingCollisionModel, StreamingMotionPack, - StreamingRenderableModel, StreamingTexture, Toc, + StreamingRenderableModel, StreamingTexture, Toc, X360StaticTextureHeader, XNGHeader, }; pub struct SoiSoup< @@ -16,9 +16,14 @@ pub struct SoiSoup< soi: Soi, } -impl = ()>, StaticTH: BinRead = ()>, MH: BinRead = ()>> SoiSoup { - pub fn cook(toc_path: &Path, soi_path: &Path) -> BinResult { - let soi = Soi::read(soi_path)?; +impl< + StreamingTH: BinRead = ()>, + StaticTH: BinRead = ()>, + MH: BinRead = ()>, + > SoiSoup +{ + pub fn cook(toc_path: &Path, soi_path: &Path, endian: Endian) -> BinResult { + let soi = Soi::read(soi_path, endian)?; let toc = Toc::read(toc_path, soi.header.version == 0x101)?; Ok(Self { toc, soi }) diff --git a/src/test.rs b/src/test.rs index 2b6ccca..ef5401a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,37 +6,75 @@ use x_flipper_360::*; use crate::textures::{GCNTextureHeader, GCTSurfaceHeader}; use crate::ComponentKind::{self, *}; -use crate::{GCGHeader, GCGHeaderArgs, XNGHeader, X360StaticTextureHeader, GCNStaticTextureHeader}; +use crate::{ + utils, DXGHeader, DXGHeaderArgs, DXTStaticTextureHeader, DXTTextureHeader, GCGHeader, + GCGHeaderArgs, GCNStaticTextureHeader, Res, X360StaticTextureHeader, XNGHeader, +}; use crate::{CollisionModelArgs, ComponentData, SoiSoup, Str, XNGHeaderArgs}; +pub type XboxSoiSoup = SoiSoup; +pub type X360SoiSoup = SoiSoup; +pub type WiiSoiSoup = SoiSoup; + #[test] fn extract() { - let toc_path = Path::new("./data/CR_03.gcn.toc"); - let soi_path = Path::new("./data/CR_03.gcn.soi"); - let str_path = Path::new("./data/CR_03.gcn.str"); - - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let res_path = Path::new("./data/FE.xbox.res"); + let str_path = Path::new("./data/FE.xbox.str"); + + let res = Res::read(res_path).unwrap(); + + let toc_path = Path::new("./data/FE.xbox.toc"); + let soi_path = Path::new("./data/FE.xbox.soi"); + + let toc_bytes = res.get_file("FE.xbox.toc".to_owned()).unwrap(); + std::fs::File::create(toc_path) + .unwrap() + .write_all(toc_bytes) + .unwrap(); + let toc_bytes = res.get_file("FE.xbox.soi".to_owned()).unwrap(); + std::fs::File::create(soi_path) + .unwrap() + .write_all(toc_bytes) + .unwrap(); + + let soup = XboxSoiSoup::cook(toc_path, soi_path, binrw::Endian::Little).unwrap(); let mut str = Str::read(str_path).unwrap(); for (id, section) in soup.find_sections().iter().enumerate() { let section_data = str.read_section_data(section).unwrap(); for component in section_data.uncached { - process_component_wii(&soup, id as u32, component); + process_component_xbox(&soup, id as u32, component); } for component in section_data.cached { - process_component_wii(&soup, id as u32, component); + process_component_xbox(&soup, id as u32, component); } } } #[test] fn dump_scn() { - let toc_path = Path::new("./data/CR_03.gcn.toc"); - let soi_path = Path::new("./data/CR_03.gcn.soi"); - let str_path = Path::new("./data/CR_03.gcn.str"); - let soup = SoiSoup::::cook(toc_path, soi_path).unwrap(); + let res_path = Path::new("./data/FE.xbox.res"); + let str_path = Path::new("./data/FE.xbox.str"); + + let res = Res::read(res_path).unwrap(); + + let toc_path = Path::new("./data/FE.xbox.toc"); + let soi_path = Path::new("./data/FE.xbox.soi"); + + let toc_bytes = res.get_file("FE.xbox.toc".to_owned()).unwrap(); + std::fs::File::create(toc_path) + .unwrap() + .write_all(toc_bytes) + .unwrap(); + let toc_bytes = res.get_file("FE.xbox.soi".to_owned()).unwrap(); + std::fs::File::create(soi_path) + .unwrap() + .write_all(toc_bytes) + .unwrap(); + + let soup = XboxSoiSoup::cook(toc_path, soi_path, binrw::Endian::Little).unwrap(); let mut str = Str::read(str_path).unwrap(); let mut num_anim_models = 1; let mut num_static_models = 1; @@ -115,20 +153,16 @@ fn print_component< } } -fn process_component_wii( - soup: &SoiSoup, - section_id: u32, - component: ComponentData, -) { +fn process_component_wii(soup: &WiiSoiSoup, section_id: u32, component: ComponentData) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.got", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.got", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header.header.write(&mut out).unwrap(); + header.header.write_be(&mut out).unwrap(); out.write_all(&component.data).unwrap(); } @@ -137,7 +171,7 @@ fn process_component_wii( .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gcg", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.gcg", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header @@ -156,7 +190,7 @@ fn process_component_wii( .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gol", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.gol", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header @@ -174,7 +208,7 @@ fn process_component_wii( if component.kind == ComponentKind::Texture { match soup.find_streaming_texture(section_id, component.id, component.instance_id) { Some(streaming_texture) => { - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gct", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.gct", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); @@ -235,20 +269,16 @@ fn process_component_wii( } } -fn process_component_xbox( - soup: &SoiSoup, - section_id: u32, - component: ComponentData, -) { +fn process_component_xbox(soup: &XboxSoiSoup, section_id: u32, component: ComponentData) { if component.kind == ComponentKind::MotionPack { let header = soup .find_motion_pack(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.got", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.mot", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - header.header.write(&mut out).unwrap(); + header.header.write_le(&mut out).unwrap(); out.write_all(&component.data).unwrap(); } @@ -257,7 +287,123 @@ fn process_component_xbox( .find_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.xng", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dxg", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .streaming_model_header + .write_options( + &mut out, + binrw::Endian::Little, + &DXGHeaderArgs { + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } + if component.kind == ComponentKind::CollisionModel { + let header = soup + .find_collision_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("./data/FE/{}.col", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header + .collision_model + .write_options( + &mut out, + binrw::Endian::Little, + &CollisionModelArgs { + ror: false, + streaming_data: component.data.clone(), + }, + ) + .unwrap(); + } + if component.kind == ComponentKind::Texture { + match soup.find_streaming_texture(section_id, component.id, component.instance_id) { + Some(streaming_texture) => { + let path = PathBuf::from(format!("./data/FE/{}.dxt", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + + // Write the version out (streamed GCTs have a flag OR'd on the version, so we set it manually to 2 [otherwise we would use streaming_texture.version]) + u32::write_options(&2, &mut out, binrw::Endian::Little, ()).unwrap(); + streaming_texture.header.write_le(&mut out).unwrap(); + + // Keeps track of our position in the streaming data. + let mut offset = 0; + let mip_count = streaming_texture.header.mip_count; + + // Holds the offsets and sizes (in a Range) for each mip so we can iterate backwards over this later + let mut mips = Vec::with_capacity(mip_count as usize); + + // Since mips are stored in forwards order (biggest to smallest) in the streaming data we need to first collect them in the vec, + for i in 0..mip_count { + let mip_width = 1.max(streaming_texture.header.width as usize / (2 as usize).pow(i)); + let mip_height = 1.max(streaming_texture.header.height as usize / (2 as usize).pow(i)); + + let mip_size = streaming_texture + .header + .format + .calculate_mip_size(mip_width, mip_height); + + mips.push(offset..offset + mip_size); + + offset += mip_size as usize; + } + + // (At this point, we should have read through the entire streaming data) + assert_eq!(offset, streaming_texture.header.calculate_image_size()); + assert_eq!(offset, component.data.len()); + + // and we can then terate over the mips in backwards order and write them to the GCT. + for i in (0..mip_count).rev() { + let mip_width = 1.max(streaming_texture.header.width as usize / (2 as usize).pow(i)); + let mip_height = 1.max(streaming_texture.header.height as usize / (2 as usize).pow(i)); + + let surface_header = GCTSurfaceHeader { + width: mip_width as u32, + height: mip_height as u32, + size: streaming_texture + .header + .format + .calculate_mip_size(mip_width, mip_height) as u32, + }; + surface_header.write_le(&mut out).unwrap(); + + component.data[mips.get(i as usize).unwrap().clone()] + .write(&mut out) + .unwrap(); + } + } + None => { + panic!("Failed to find texture header."); + } + } + } +} + +fn process_component_xbox360(soup: &X360SoiSoup, section_id: u32, component: ComponentData) { + if component.kind == ComponentKind::MotionPack { + let header = soup + .find_motion_pack(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("./data/FE/{}.got", component.path)); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut out = std::fs::File::create(path).unwrap(); + header.header.write_be(&mut out).unwrap(); + out.write_all(&component.data).unwrap(); + } + + if component.kind == ComponentKind::RenderableModel { + let header = soup + .find_model(section_id, component.id, component.instance_id) + .unwrap(); + + let path = PathBuf::from(format!("./data/FE/{}.xng", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header @@ -276,7 +422,7 @@ fn process_component_xbox( .find_collision_model(section_id, component.id, component.instance_id) .unwrap(); - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.gol", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.gol", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); header @@ -314,7 +460,7 @@ fn process_component_xbox( mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -336,7 +482,7 @@ fn process_component_xbox( mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -358,7 +504,7 @@ fn process_component_xbox( mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -380,7 +526,7 @@ fn process_component_xbox( mip_address: metadata.mip_address(), }; - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); x_flipper_360::convert_to_dds(&config, &component.data, &mut out).unwrap(); @@ -389,12 +535,14 @@ fn process_component_xbox( } None => match soup.find_static_texture(section_id, component.id, component.instance_id) { Some(static_texture) => { - let path = PathBuf::from(format!(".\\data\\CR_03\\{}.dds", component.path)); + let path = PathBuf::from(format!("./data/FE/{}.dds", component.path)); std::fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut out = std::fs::File::create(path).unwrap(); - out.write_all(&static_texture.static_texture_header.header_file).unwrap(); + out + .write_all(&static_texture.static_texture_header.header_file) + .unwrap(); } - None => panic!("Failed to find texture header."), + None => println!("Failed to find texture header."), }, } } diff --git a/src/textures/dds.rs b/src/textures/dds.rs index 2cf3771..7a86759 100644 --- a/src/textures/dds.rs +++ b/src/textures/dds.rs @@ -5,4 +5,4 @@ pub struct X360StaticTextureHeader { pub dds_size: u32, #[br(count = dds_size)] pub header_file: Vec, -} \ No newline at end of file +} diff --git a/src/textures/dxt.rs b/src/textures/dxt.rs new file mode 100644 index 0000000..c94a937 --- /dev/null +++ b/src/textures/dxt.rs @@ -0,0 +1,140 @@ +use binrw::*; + +use crate::div_round_up; + +#[derive(BinRead, BinWrite, PartialEq, Debug, Clone)] +#[brw(repr = u32)] +pub enum DXTFormat { + Dxt1 = 37, + Dxt1Mm = 38, + Dxt2 = 47, + Dxt2Mm = 48, + Dxt3 = 49, + Dxt3Mm = 50, + Dxt4 = 51, + Dxt4Mm = 52, + Dxt5 = 53, + Dxt5Mm = 54, + Pal8 = 55, + Pal8Mm = 56, + Lum8 = 57, +} + +impl DXTFormat { + pub fn get_block_dim(&self) -> (usize, usize) { + match self { + DXTFormat::Pal8 => (1, 1), + DXTFormat::Pal8Mm => (1, 1), + DXTFormat::Lum8 => (1, 1), + _ => (4, 4), + } + } + pub fn get_bits_per_pixel(&self) -> usize { + match self { + DXTFormat::Dxt1 => 4, + DXTFormat::Dxt1Mm => 4, + DXTFormat::Dxt2 => 8, + DXTFormat::Dxt2Mm => 8, + DXTFormat::Dxt3 => 8, + DXTFormat::Dxt3Mm => 8, + DXTFormat::Dxt4 => 4, + DXTFormat::Dxt4Mm => 4, + DXTFormat::Dxt5 => 8, + DXTFormat::Dxt5Mm => 8, + DXTFormat::Pal8 => 8, + DXTFormat::Pal8Mm => 8, + DXTFormat::Lum8 => 8, + } + } + pub fn calculate_mip_size(&self, width: usize, height: usize) -> usize { + let (blk_width_pixels, blk_height_pixels) = self.get_block_dim(); + let bits_per_pixel = self.get_bits_per_pixel(); + let blk_size_bytes = ((blk_width_pixels * blk_height_pixels) * bits_per_pixel) / 8; + let size_bytes = div_round_up(width, blk_width_pixels) + * div_round_up(height, blk_height_pixels) + * blk_size_bytes; + + size_bytes + } +} + +impl std::fmt::Display for DXTFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DXTFormat::Dxt1 | DXTFormat::Dxt1Mm => write!(f, "Dxt1"), + DXTFormat::Dxt2 | DXTFormat::Dxt2Mm => write!(f, "Dxt2"), + DXTFormat::Dxt3 | DXTFormat::Dxt3Mm => write!(f, "Dxt3"), + DXTFormat::Dxt4 | DXTFormat::Dxt4Mm => write!(f, "Dxt4"), + DXTFormat::Dxt5 | DXTFormat::Dxt5Mm => write!(f, "Dxt5"), + DXTFormat::Pal8 | DXTFormat::Pal8Mm => write!(f, "Pal8"), + DXTFormat::Lum8 => write!(f, "Lum8"), + } + } +} + +#[derive(BinRead, BinWrite, Debug, Clone)] +pub struct DXTTextureHeader { + pub format: DXTFormat, + pub palette_size: u32, + + #[br(count = palette_size * 2)] + pub palette: Vec, + + pub mip_count: u32, + pub width: u32, + pub height: u32, +} + +#[derive(BinRead, BinWrite, Debug)] +pub struct DXTSurfaceHeader { + pub width: u32, + pub height: u32, + pub size: u32, +} + +impl DXTTextureHeader { + pub fn calculate_image_size(&self) -> usize { + let (blk_width_pixels, blk_height_pixels) = self.format.get_block_dim(); + let bits_per_pixel = self.format.get_bits_per_pixel(); + let blk_size_bytes = ((blk_width_pixels * blk_height_pixels) * bits_per_pixel) / 8; + let mut size_bytes = div_round_up(self.width as usize, blk_width_pixels) + * div_round_up(self.height as usize, blk_height_pixels) + * blk_size_bytes; + + for i in 1..self.mip_count { + let mip_width = self.width as usize / (2 as usize).pow(i); + let mip_height = self.height as usize / (2 as usize).pow(i); + + size_bytes += div_round_up(mip_width as usize, blk_width_pixels) + * div_round_up(mip_height as usize, blk_height_pixels) + * blk_size_bytes; + } + + size_bytes + } +} + +#[derive(BinRead, BinWrite)] +pub struct DXTSurface { + pub header: DXTSurfaceHeader, + + #[br(count = header.size)] + pub data: Vec, +} + +#[derive(BinRead, BinWrite)] +pub struct DXTStaticTextureHeader { + pub version: u32, + pub format: DXTFormat, + pub palette_size: u32, + + #[br(count = palette_size * 2)] + pub palette: Vec, + + pub mip_count: u32, + pub width: u32, + pub height: u32, + + #[br(count = mip_count)] + pub mips: Vec, +} diff --git a/src/textures/gct.rs b/src/textures/gct.rs index 342d8be..7e0eadb 100644 --- a/src/textures/gct.rs +++ b/src/textures/gct.rs @@ -121,5 +121,5 @@ pub struct GCNStaticTextureHeader { pub height: u32, #[br(count = mip_count)] - pub mips: Vec -} \ No newline at end of file + pub mips: Vec, +} diff --git a/src/textures/mod.rs b/src/textures/mod.rs index 7874a72..b6ab637 100644 --- a/src/textures/mod.rs +++ b/src/textures/mod.rs @@ -1,4 +1,6 @@ -mod gct; mod dds; +mod dxt; +mod gct; +pub use self::dds::*; +pub use self::dxt::*; pub use self::gct::*; -pub use self::dds::*; \ No newline at end of file diff --git a/src/toc.rs b/src/toc.rs index 7b84001..fb4c20f 100644 --- a/src/toc.rs +++ b/src/toc.rs @@ -109,6 +109,11 @@ impl Toc { Self::read_file(&mut file, is_new) } + pub fn read_le(path: &Path, is_new: bool) -> BinResult { + let mut file = File::open(path)?; + Self::read_file_le(&mut file, is_new) + } + pub fn read_file(file: &mut File, is_new: bool) -> BinResult { let mut sections = Vec::new(); let file_size = file.metadata()?.len(); @@ -124,6 +129,21 @@ impl Toc { Ok(Self { sections }) } + pub fn read_file_le(file: &mut File, is_new: bool) -> BinResult { + let mut sections = Vec::new(); + let file_size = file.metadata()?.len(); + + // read sections until the end of the file is reached + while file.stream_position()? < file_size { + // hack that allows newer SOI packages to load + let read_zlib_header = !is_new; + let section = Section::read_le_args(file, binrw::args! {read_zlib_header})?; + sections.push(section); + } + + Ok(Self { sections }) + } + pub fn find_section(&self, id: u32) -> Option<&Section> { self.sections.get(id as usize) } diff --git a/src/utils.rs b/src/utils.rs index 1e26795..b3121a4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,6 @@ use binrw::{BinRead, BinWrite}; #[derive(Default, BinRead, BinWrite, Debug)] -#[brw(big)] pub struct Vector4 { pub x: f32, pub y: f32, @@ -20,7 +19,6 @@ impl std::fmt::Display for Vector4 { } #[derive(Default, BinRead, BinWrite, Debug, Clone, Copy)] -#[brw(big)] pub struct Vector3 { pub x: f32, pub y: f32, From eff60aabd15aa95036939082db22a8c8fb6e8161 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:23:36 -0500 Subject: [PATCH 22/23] Add endian argument to `Toc` reader. --- src/soi_soup.rs | 2 +- src/toc.rs | 32 ++++++-------------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/soi_soup.rs b/src/soi_soup.rs index c129792..f275099 100644 --- a/src/soi_soup.rs +++ b/src/soi_soup.rs @@ -24,7 +24,7 @@ impl< { pub fn cook(toc_path: &Path, soi_path: &Path, endian: Endian) -> BinResult { let soi = Soi::read(soi_path, endian)?; - let toc = Toc::read(toc_path, soi.header.version == 0x101)?; + let toc = Toc::read(toc_path, endian, soi.header.version == 0x101)?; Ok(Self { toc, soi }) } diff --git a/src/toc.rs b/src/toc.rs index fb4c20f..4e978d0 100644 --- a/src/toc.rs +++ b/src/toc.rs @@ -2,7 +2,7 @@ use std::fs::File; use std::io::Seek; use std::path::Path; -use binrw::{BinRead, BinResult}; +use binrw::{BinRead, BinResult, Endian}; use crate::utils::clean_path; @@ -104,17 +104,12 @@ pub struct Toc { } impl Toc { - pub fn read(path: &Path, is_new: bool) -> BinResult { + pub fn read(path: &Path, endian: Endian, is_new: bool) -> BinResult { let mut file = File::open(path)?; - Self::read_file(&mut file, is_new) + Self::read_file(&mut file, endian, is_new) } - pub fn read_le(path: &Path, is_new: bool) -> BinResult { - let mut file = File::open(path)?; - Self::read_file_le(&mut file, is_new) - } - - pub fn read_file(file: &mut File, is_new: bool) -> BinResult { + pub fn read_file(file: &mut File, endian: Endian, is_new: bool) -> BinResult { let mut sections = Vec::new(); let file_size = file.metadata()?.len(); @@ -122,28 +117,13 @@ impl Toc { while file.stream_position()? < file_size { // hack that allows newer SOI packages to load let read_zlib_header = !is_new; - let section = Section::read_be_args(file, binrw::args! {read_zlib_header})?; + let section = Section::read_options(file, endian, binrw::args! {read_zlib_header})?; sections.push(section); } Ok(Self { sections }) } - - pub fn read_file_le(file: &mut File, is_new: bool) -> BinResult { - let mut sections = Vec::new(); - let file_size = file.metadata()?.len(); - - // read sections until the end of the file is reached - while file.stream_position()? < file_size { - // hack that allows newer SOI packages to load - let read_zlib_header = !is_new; - let section = Section::read_le_args(file, binrw::args! {read_zlib_header})?; - sections.push(section); - } - - Ok(Self { sections }) - } - + pub fn find_section(&self, id: u32) -> Option<&Section> { self.sections.get(id as usize) } From 27f94837fedf234b44070860771bb39aefb4ac51 Mon Sep 17 00:00:00 2001 From: itsmeft24 <57544858+itsmeft24@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:45:22 -0500 Subject: [PATCH 23/23] Update binrw --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 27d38cc..5cf52dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] modular-bitfield = "0.11" flate2 = "1.0" -binrw = "0.11.2" +binrw = "0.13" [dev-dependencies] x-flipper-360 = { git = "https://github.com/offsetting/x-flipper-360" }