Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions crates/asset-server/src/types/msb.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy::{asset::LoadContext, prelude::*};
use fstools_formats::msb::{parts::PartData, Msb, MsbError};
use fstools_formats::msb::{parts, parts::PartData, Msb, MsbError, MsbVersion::EldenRing};
use thiserror::Error;

use crate::{asset_source::fast_path::FastPathAssetLoader, types::flver::FlverAsset};
Expand Down Expand Up @@ -78,7 +78,7 @@ impl FastPathAssetLoader for MsbAssetLoader {
_settings: &'a Self::Settings,
load_context: &'a mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let msb = Msb::parse(reader)?;
let msb = Msb::parse(reader, &EldenRing)?;

let models = msb
.models()
Expand Down Expand Up @@ -126,7 +126,9 @@ impl FastPathAssetLoader for MsbAssetLoader {
.filter_map(|p| {
let part = p.as_ref().expect("Could not get point entry from MSB");

if let PartData::DummyAsset(_) = part.part {
if let PartData::EldenRing(parts::elden_ring::PartData::DummyAsset(_)) =
part.part
{
return None;
}

Expand Down
19 changes: 6 additions & 13 deletions crates/cli/src/bin/msb-test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::{error::Error, io::Read, path::PathBuf};

use clap::Parser;
use fstools_dvdbnd::{DvdBnd, FileKeyProvider};
use fstools_dvdbnd::{DvdBnd, FileKeyProvider, GameType::EldenRing};
use fstools_formats::{
dcx::DcxHeader,
msb::{point::PointData, Msb},
msb::{point, point::PointData, Msb, MsbVersion},
};

#[derive(Parser, Debug)]
Expand All @@ -20,15 +20,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let er_path = args.erpath;

let keys = FileKeyProvider::new("keys");
let archives = [
er_path.join("Data0"),
er_path.join("Data1"),
er_path.join("Data2"),
er_path.join("Data3"),
er_path.join("sd/sd"),
];

let vfs = DvdBnd::create(archives.clone(), &keys).expect("unable to create vfs");
let vfs = DvdBnd::create_from_game(EldenRing, er_path, keys).expect("unable to create vfs");

for msb_path in MSBS.iter() {
// println!("Parsing MSB {}", msb_path);
Expand All @@ -40,10 +33,10 @@ fn main() -> Result<(), Box<dyn Error>> {
let mut decompressed = Vec::with_capacity(decoder.hint_size());
decoder.read_to_end(&mut decompressed)?;

let msb = Msb::parse(&decompressed).expect("Could not parse MSB");
let msb = Msb::parse(&decompressed, &MsbVersion::EldenRing).expect("Could not parse MSB");

for point in msb.points().expect("Could not get point set from MSB") {
if let PointData::Message(message) =
if let PointData::EldenRing(point::elden_ring::PointData::Message(message)) =
point.expect("Could not retrieve point from MSB").point
{
println!(
Expand All @@ -59,7 +52,7 @@ fn main() -> Result<(), Box<dyn Error>> {
Ok(())
}

const MSBS: [&str; 1409] = [
static MSBS: [&str; 1409] = [
"/map/mapstudio/m10_00_00_00.msb.dcx",
"/map/mapstudio/m10_00_00_99.msb.dcx",
"/map/mapstudio/m10_01_00_00.msb.dcx",
Expand Down
216 changes: 203 additions & 13 deletions crates/cli/src/describe.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
use std::{
error::Error,
io::{Cursor, Read},
};
use std::{error::Error, io::Cursor};

use fstools_dvdbnd::DvdBnd;
use fstools_formats::{bnd4::BND4, dcx::DcxHeader, entryfilelist::EntryFileList};

pub fn describe_bnd(dvd_bnd: &DvdBnd, name: &str) -> Result<(), Box<dyn Error>> {
let (dcx, mut reader) = DcxHeader::read(dvd_bnd.open(name)?)?;
use fstools_formats::{
bnd4::BND4,
entryfilelist::EntryFileList,
flver::reader::FLVER,
msb,
msb::{
event,
event::EventType,
parts,
parts::PartType,
point,
point::PointType,
MsbError, MsbParam, MsbVersion,
MsbVersion::{EldenRing, Nightreign},
},
};

let mut data = vec![];
reader.read_to_end(&mut data)?;
use crate::GameType;

pub fn describe_bnd(
dvd_bnd: &DvdBnd,
name: &str,
nested_bnd_names: &[String],
) -> Result<(), Box<dyn Error>> {
let (dcx, data) = dvd_bnd.read_file(nested_bnd_names, name)?;
let bnd = BND4::from_reader(&mut Cursor::new(data))?;

println!("Compression type: {:?}", dcx.compression_parameters());
println!("Compression type: {}", dcx);
println!("Files: {}", bnd.files.len());

for idx in 0..bnd.files.len() {
Expand Down Expand Up @@ -47,6 +61,182 @@ pub fn describe_entryfilelist(dvd_bnd: &DvdBnd, name: &str) -> Result<(), Box<dy
Ok(())
}

pub fn describe_matbin(_dvd_bnd: &DvdBnd, _name: &str) -> Result<(), Box<dyn Error>> {
todo!()
pub fn describe_flver(
dvd_bnd: &DvdBnd,
name: &str,
nested_bnd_names: &[String],
) -> Result<(), Box<dyn Error>> {
let (dcx, data) = dvd_bnd.read_file(nested_bnd_names, name)?;
let flver = FLVER::from_reader(&mut Cursor::new(data))?;

println!("Compression type: {}", dcx);
println!("Version: 0x{:X}", flver.version);
println!("Bounding Box Min: {}", flver.bounding_box_min);
println!("Bounding Box Max: {}", flver.bounding_box_max);
println!("Faces: {}", flver.face_count);
println!("Index Buffers: {}", flver.face_sets.len());
println!("Vertex Buffers: {}", flver.vertex_buffers.len());
println!("Bones: {}", flver.bones.len());
println!("Dummies: {}", flver.dummies.len());

println!("Materials: {}", flver.materials.len());
for idx in 0..flver.materials.len() {
println!("Material[{idx}] {}", flver.materials[idx].mtd);
}

println!("Meshes: {}", flver.meshes.len());
for idx in 0..flver.meshes.len() {
print!("Mesh[{idx}]");
print!(" bone: {},", flver.meshes[idx].default_bone_index);
print!(" material: {},", flver.meshes[idx].material_index);
print!(" dynamic: {},", flver.meshes[idx].dynamic);
print!(
" Index Buffers: {:?},",
flver.meshes[idx].face_set_indices.as_slice()
);
println!(
" Vertex Buffers: {:?}",
flver.meshes[idx].vertex_buffer_indices.as_slice()
);
}

Ok(())
}

pub fn describe_matbin(
dvd_bnd: &DvdBnd,
name: &str,
nested_bnd_names: &[String],
) -> Result<(), Box<dyn Error>> {
let (dcx, data) = dvd_bnd.read_file(nested_bnd_names, name)?;
let matbin = fstools_formats::matbin::Matbin::parse(&data)
.expect("Could not parse data as matbin");

println!("Compression type: {}", dcx);
println!("Shader: {}", matbin.shader_path().expect("No shader path"));
println!("Source: {}", matbin.source_path().expect("No source path"));
let mut params = matbin.parameters();
let param_count: usize = matbin.parameters().count();
println!("Parameters: {}", param_count);
for idx in 0..param_count {
if let Some(Ok(param)) = params.next() {
println!("Parameter[{idx}] {0} = {1:?}", param.name, param.value);
}
}
let mut samplers = matbin.samplers();
let sampler_count: usize = matbin.samplers().count();
println!("Samplers: {}", sampler_count);
for idx in 0..sampler_count {
if let Some(Ok(sampler)) = samplers.next() {
println!("Sampler[{idx}] {0}: {1}", sampler.name, sampler.path);
}
}

Ok(())
}

pub fn describe_msb(
dvd_bnd: &DvdBnd,
name: &str,
nested_bnd_names: &[String],
game_type: &GameType,
) -> Result<(), Box<dyn Error>> {
let (dcx, data) = dvd_bnd.read_file(nested_bnd_names, name)?;
let version: MsbVersion = match game_type {
GameType::ErPc => EldenRing,
GameType::NrPc => Nightreign,
};
let msb = msb::Msb::parse(&data, &version).expect("Could not parse data as msb");

println!("Compression type: {}", dcx);

if let Ok(models) = msb.models() {
let models_vec = Vec::from_iter(models);
println!("Models: {}", models_vec.len());
for idx in 0..models_vec.len() {
if let Some(Ok(model)) = models_vec.get(idx) {
println!(" Model[{idx}] {}", model.name());
}
}
}

match version {
EldenRing => {
if let Ok(events) = msb.events() {
println!("Events: {}", events.count());
for ty in event::elden_ring::EventType::variants() {
print_msb_param_group(msb.events(), EventType::EldenRing(ty.0), ty.1);
}
}

if let Ok(points) = msb.points() {
println!("Points: {}", points.count());
for ty in point::elden_ring::PointType::variants() {
print_msb_param_group(msb.points(), PointType::EldenRing(ty.0), ty.1);
}
}

if let Ok(parts) = msb.parts() {
println!("Parts: {}", parts.count());
for ty in parts::elden_ring::PartType::variants() {
print_msb_param_group(msb.parts(), PartType::EldenRing(ty.0), ty.1);
}
}
}
Nightreign => {
if let Ok(events) = msb.events() {
println!("Events: {}", events.count());
for ty in event::nightreign::EventType::variants() {
print_msb_param_group(msb.events(), EventType::Nightreign(ty.0), ty.1);
}
}

if let Ok(points) = msb.points() {
println!("Points: {}", points.count());
for ty in point::nightreign::PointType::variants() {
print_msb_param_group(msb.points(), PointType::Nightreign(ty.0), ty.1);
}
}

if let Ok(parts) = msb.parts() {
println!("Parts: {}", parts.count());
for ty in parts::nightreign::PartType::variants() {
print_msb_param_group(msb.parts(), PartType::Nightreign(ty.0), ty.1);
}
}
}
}

if let Ok(routes) = msb.routes() {
let route_vec = Vec::from_iter(routes);
println!("Routes: {}", route_vec.len());
for idx in 0..route_vec.len() {
if let Some(Ok(route)) = route_vec.get(idx) {
println!(" Route[{idx}] {}", route.name());
}
}
}

Ok(())
}

fn print_msb_param_group<'a, P, T>(
params: Result<impl Iterator<Item = Result<P, MsbError>>, MsbError>,
group_type: T,
group_name: &str,
) where
P: MsbParam<'a, P, T>,
{
let param_group = P::of_type(params, group_type);
if !param_group.is_empty() {
println!(" {0}: {1}", group_name, param_group.len());
}
for param in param_group {
println!(
" {0}[{1}] {2}",
group_name,
param.type_index(),
param.name()
);
}
}
33 changes: 25 additions & 8 deletions crates/cli/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,27 @@ use fstools_formats::{bnd4::BND4, dcx::DcxHeader};
use indicatif::{ParallelProgressIterator, ProgressStyle};
use rayon::prelude::*;

use crate::GameType;

pub fn extract(
dvd_bnd: &DvdBnd,
recursive: bool,
filter: Option<String>,
output_path: PathBuf,
game_type: GameType,
) -> Result<(), Box<dyn Error>> {
let lines = fstools_elden_ring_support::dictionary()
let output_game_ext = match game_type {
GameType::ErPc => "er-pc",
GameType::NrPc => "nr-pc",
};

let lines = DvdBnd::dictionary_from_game(game_type.into())
.filter(|line| {
filter
.as_ref()
.map(|filter| line.to_string_lossy().contains(filter))
.unwrap_or(true)
})
.map(std::path::PathBuf::from)
.collect::<Vec<_>>();

let style = ProgressStyle::with_template("[{elapsed_precise}] {bar:40} {pos:>7}/{len:7} {msg}")
Expand All @@ -41,9 +48,11 @@ pub fn extract(
let path = path.strip_prefix("/").expect("no leading slash");
let parent_path = if is_archive {
// twice to strip "bnd.dcx"
output_path.join(path.with_extension("").with_extension(""))
output_path
.join(output_game_ext)
.join(path.with_extension("").with_extension(""))
} else {
output_path.to_path_buf()
output_path.join(output_game_ext).to_path_buf()
};

let _ = fs::create_dir_all(&parent_path);
Expand Down Expand Up @@ -72,10 +81,18 @@ pub fn extract(
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer)?;

fs::write(
parent_path.join(path.file_name().expect("no file name")),
buffer,
)?;
let parent_dir = path.parent();
if let Some(parent_dir) = parent_dir {
if let Ok(false) = fs::exists(parent_dir) {
if fs::create_dir_all(
output_path.join(output_game_ext).join(parent_dir),
)
.is_ok()
{
fs::write(parent_path.join(path), buffer)?;
}
}
}

Ok::<_, Box<dyn Error + Send + Sync>>(total + 1)
}
Expand Down
Loading
Loading