Skip to content

Commit

Permalink
base64 images and sounds, normal meshes and text + export preview
Browse files Browse the repository at this point in the history
  • Loading branch information
pizzart committed Oct 10, 2023
1 parent f910397 commit 231b34b
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 125 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions bff-gui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ tauri-build = { version = "1.3", features = [] }

[dependencies]
ansi-to-html = "0.1.3"
base64 = "0.21.4"
bff = { path = "../../bff" }
binrw = "0.11.2"
ddsfile = "0.5.1"
Expand All @@ -24,6 +25,7 @@ image_dds = "0.1.1"
quick-xml = { version = "0.30.0", features = ["serialize"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.104"
serde_repr = "0.1.16"
structstruck = "0.4.1"
tauri = { version = "1.3", features = [
"dialog-message",
Expand Down
32 changes: 25 additions & 7 deletions bff-gui/src-tauri/src/bitmap.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
use std::io::{BufReader, Cursor};
use std::path::Path;

use base64::{engine::general_purpose, Engine as _};
use bff::class::bitmap::Bitmap;
use bff::names::Name;

use crate::error::BffGuiResult;
use crate::traits::Export;
use crate::{DataType, PreviewData};

impl Export for Box<Bitmap> {
fn export(&self, export_path: &Path, _name: Name) -> BffGuiResult<String> {
fn export(&self, _export_path: &Path, _name: Name) -> BffGuiResult<PreviewData> {
match **self {
Bitmap::BitmapV1_381_67_09PC(ref bitmap) => {
let buf = BufReader::new(Cursor::new(&bitmap.body.data));
let dds = ddsfile::Dds::read(buf)?;
let image = image_dds::image_from_dds(&dds, 0)?;
image.save(export_path)?;
Ok(serde_json::to_string_pretty(&bitmap.body)?)
let mut bytes: Vec<u8> = Vec::new();
image.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png)?;
Ok(PreviewData {
is_base64: true,
data: general_purpose::STANDARD_NO_PAD.encode(bytes),
data_type: DataType::Image,
})
}
Bitmap::BitmapV1_291_03_06PC(ref bitmap) => {
let buf = BufReader::new(Cursor::new(&bitmap.body.data));
let dds = ddsfile::Dds::read(buf)?;
let image = image_dds::image_from_dds(&dds, 0)?;
image.save(export_path)?;
Ok(serde_json::to_string_pretty(&bitmap.body)?)
// image.save(export_path)?;
let mut bytes: Vec<u8> = Vec::new();
image.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png)?;
Ok(PreviewData {
is_base64: true,
data: general_purpose::STANDARD_NO_PAD.encode(bytes),
data_type: DataType::Image,
})
}
Bitmap::BitmapV1_06_63_02PC(ref bitmap) => {
let image: image::ImageBuffer<_, _> = match &bitmap.body.dds {
Expand Down Expand Up @@ -78,8 +91,13 @@ impl Export for Box<Bitmap> {
}
}
};
image.save(export_path)?;
Ok(serde_json::to_string_pretty(&bitmap.body)?)
let mut bytes: Vec<u8> = Vec::new();
image.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png)?;
Ok(PreviewData {
is_base64: true,
data: general_purpose::STANDARD_NO_PAD.encode(bytes),
data_type: DataType::Image,
})
}
}
}
Expand Down
1 change: 1 addition & 0 deletions bff-gui/src-tauri/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub enum Error {
Image(image::error::ImageError),
Hound(hound::Error),
De(quick_xml::DeError),
Decode(base64::DecodeError),
}

impl serde::Serialize for Error {
Expand Down
67 changes: 49 additions & 18 deletions bff-gui/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::io::{BufReader, Write};
use std::path::{Path, PathBuf};
use std::sync::Mutex;

use base64::{engine::general_purpose, Engine as _};
use bff::bigfile::resource::Resource;
use bff::bigfile::BigFile;
use bff::class::user_define::UserDefine;
Expand All @@ -16,6 +17,7 @@ use bff::platforms::Platform;
use bff::traits::TryIntoVersionPlatform;
use error::{BffGuiResult, InvalidPreviewError, InvalidResourceError};
use serde::Serialize;
use serde_repr::Serialize_repr;
use traits::Export;

use crate::error::Error;
Expand All @@ -29,16 +31,32 @@ mod traits;
#[derive(Debug, Serialize, Clone)]
pub struct ResourcePreview {
name: Name,
preview_data: String,
preview_path: Option<PathBuf>,
preview_json: String,
preview_data: Option<PreviewData>,
}

impl std::fmt::Display for ResourcePreview {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.preview_data)
write!(f, "{}", self.preview_json)
}
}

#[derive(Debug, Serialize, Clone)]
pub struct PreviewData {
is_base64: bool,
data: String,
data_type: DataType,
}

#[derive(Debug, Serialize_repr, Clone, Copy)]
#[repr(u8)]
enum DataType {
Image = 0,
Sound = 1,
Mesh = 2,
Text = 3,
}

#[derive(Serialize)]
struct BigFileData {
filename: String,
Expand Down Expand Up @@ -71,8 +89,8 @@ fn main() {
.invoke_handler(tauri::generate_handler![
extract_bigfile,
parse_resource,
export_all_objects,
export_one_object,
export_all_json,
export_one_json,
export_preview,
get_extensions,
])
Expand Down Expand Up @@ -135,42 +153,46 @@ fn parse_resource(

let resource: &Resource = bf.objects.get(&resource_name).unwrap();

let (data, path) = match resource.try_into_version_platform(version.clone(), platform)? {
let class = resource.try_into_version_platform(version.clone(), platform)?;
let data = match class {
Class::Bitmap(ref bitmap) => {
let new_path = temp_path.join(format!("{}.png", resource.name));
(bitmap.export(&new_path, resource_name)?, Some(new_path))
Some(bitmap.export(&new_path, resource_name)?)
}
Class::Sound(ref sound) => {
let new_path = temp_path.join(format!("{}.wav", resource.name));
(sound.export(&new_path, resource_name)?, Some(new_path))
Some(sound.export(&new_path, resource_name)?)
}
Class::Mesh(ref mesh) => {
let new_path = temp_path.join(format!("{}.dae", resource.name));
(mesh.export(&new_path, resource_name)?, Some(new_path))
Some(mesh.export(&new_path, resource_name)?)
}
Class::UserDefine(ref userdefine) => match **userdefine {
UserDefine::UserDefineV1_291_03_06PC(ref userdefine) => {
(userdefine.body.data.to_string(), None)
}
UserDefine::UserDefineV1_291_03_06PC(ref userdefine) => Some(PreviewData {
is_base64: false,
data: userdefine.body.data.to_string(),
data_type: DataType::Text,
}),
},
// Class::Material(material) => match *material {
// Material::MaterialV1_291_03_06PC(material) => {}
// _ => (),
// },
class => (serde_json::to_string_pretty(&class)?, None),
_ => None,
};
let json = serde_json::to_string_pretty(&class)?;

let new_object = ResourcePreview {
name: resource_name,
preview_json: json,
preview_data: data,
preview_path: path,
};
state.add_preview(new_object.clone());
Ok(new_object)
}

#[tauri::command]
fn export_all_objects(path: &Path, state: tauri::State<AppState>) -> BffGuiResult<()> {
fn export_all_json(path: &Path, state: tauri::State<AppState>) -> BffGuiResult<()> {
let mut state_guard = state.0.lock().unwrap();
let state = state_guard.as_mut().unwrap();
for resource in state.bigfile.objects.values() {
Expand All @@ -185,7 +207,7 @@ fn export_all_objects(path: &Path, state: tauri::State<AppState>) -> BffGuiResul
}

#[tauri::command]
fn export_one_object(path: &Path, name: Name, state: tauri::State<AppState>) -> BffGuiResult<()> {
fn export_one_json(path: &Path, name: Name, state: tauri::State<AppState>) -> BffGuiResult<()> {
let mut state_guard = state.0.lock().unwrap();
let state = state_guard.as_mut().unwrap();
let resource: &Resource = state
Expand Down Expand Up @@ -213,11 +235,20 @@ fn write_class(path: &PathBuf, class: &Class) -> BffGuiResult<()> {
fn export_preview(path: &Path, name: Name, state: tauri::State<AppState>) -> BffGuiResult<()> {
let mut state_guard = state.0.lock().unwrap();
let state = state_guard.as_mut().unwrap();
let preview_object: &ResourcePreview = state
let resource_preview: &ResourcePreview = state
.resource_previews
.get(&name)
.ok_or(InvalidPreviewError::new(name))?;
std::fs::copy(preview_object.preview_path.as_ref().unwrap(), path)?;
let preview_data = resource_preview
.preview_data
.as_ref()
.ok_or(InvalidPreviewError::new(name))?;
let binding = general_purpose::STANDARD_NO_PAD.decode(&preview_data.data)?;
let written_data = match preview_data.is_base64 {
true => &binding,
false => preview_data.data.as_bytes(),
};
File::create(path)?.write(written_data)?;
Ok(())
}

Expand Down
18 changes: 11 additions & 7 deletions bff-gui/src-tauri/src/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fs::File;
use std::io::Write;
use std::io::Cursor;
use std::path::Path;
use std::str::from_utf8;

use bff::class::mesh::{v1_291_03_06_pc, Mesh};
use bff::names::Name;
Expand All @@ -10,6 +10,7 @@ use serde::Serialize;

use crate::error::{BffGuiResult, UnimplementedExporterError};
use crate::traits::Export;
use crate::{DataType, PreviewData};

structstruck::strike! {
#[strikethrough[derive(Serialize)]]
Expand Down Expand Up @@ -122,7 +123,7 @@ struct SimpleMesh {
}

impl Export for Box<Mesh> {
fn export(&self, export_path: &Path, name: Name) -> BffGuiResult<String> {
fn export(&self, export_path: &Path, name: Name) -> BffGuiResult<PreviewData> {
match **self {
Mesh::MeshV1_291_03_06PC(ref mesh) => {
let buffers: Vec<SimpleMesh> = mesh
Expand Down Expand Up @@ -305,11 +306,14 @@ impl Export for Box<Mesh> {
},
};

let mut buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 2);
let mut bytes = Vec::new();
let mut writer = Writer::new_with_indent(Cursor::new(&mut bytes), b' ', 2);
writer.write_serializable("COLLADA", &collada)?;
File::create(export_path)?.write_all(&buffer)?;
Ok(serde_json::to_string_pretty(&mesh.link_header)?)
Ok(PreviewData {
is_base64: true,
data: from_utf8(&bytes).unwrap().to_string(),
data_type: DataType::Mesh,
})
}
_ => Err(UnimplementedExporterError::new(name, Mesh::NAME).into()),
}
Expand Down
37 changes: 26 additions & 11 deletions bff-gui/src-tauri/src/sound.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use std::io::Cursor;
use std::path::Path;

use base64::{engine::general_purpose, Engine as _};
use bff::class::sound::Sound;
use bff::names::Name;

use crate::error::BffGuiResult;
use crate::traits::Export;
use crate::{DataType, PreviewData};

impl Export for Box<Sound> {
fn export(&self, export_path: &Path, _name: Name) -> BffGuiResult<String> {
fn export(&self, _export_path: &Path, _name: Name) -> BffGuiResult<PreviewData> {
match **self {
Sound::SoundV1_291_03_06PC(ref sound) => {
let spec = hound::WavSpec {
Expand All @@ -20,16 +23,22 @@ impl Export for Box<Sound> {
sample_format: hound::SampleFormat::Int,
};

let mut parent_writer = hound::WavWriter::create(export_path, spec).unwrap();
let mut writer = parent_writer.get_i16_writer(sound.body.data.len() as u32);
let mut bytes = Vec::new();
let mut cursor = Cursor::new(&mut bytes); //TODO: use bufwriter
let mut parent_writer = hound::WavWriter::new(&mut cursor, spec).unwrap();
let mut sample_writer = parent_writer.get_i16_writer(sound.body.data.len() as u32);

for sample in &sound.body.data {
writer.write_sample(*sample);
sample_writer.write_sample(*sample);
}
writer.flush()?;
sample_writer.flush()?;
parent_writer.finalize()?;

Ok(serde_json::to_string_pretty(&sound.body)?)
Ok(PreviewData {
is_base64: true,
data: general_purpose::STANDARD_NO_PAD.encode(bytes),
data_type: DataType::Sound,
})
}
Sound::SoundV1_381_67_09PC(ref sound) => {
let spec = hound::WavSpec {
Expand All @@ -42,16 +51,22 @@ impl Export for Box<Sound> {
sample_format: hound::SampleFormat::Int,
};

let mut parent_writer = hound::WavWriter::create(export_path, spec).unwrap();
let mut writer = parent_writer.get_i16_writer(sound.body.data.len() as u32);
let mut bytes = Vec::new();
let mut cursor = Cursor::new(&mut bytes);
let mut parent_writer = hound::WavWriter::new(&mut cursor, spec).unwrap();
let mut sample_writer = parent_writer.get_i16_writer(sound.body.data.len() as u32);

for sample in &sound.body.data {
writer.write_sample(*sample);
sample_writer.write_sample(*sample);
}
writer.flush()?;
sample_writer.flush()?;
parent_writer.finalize()?;

Ok(serde_json::to_string_pretty(&sound.body)?)
Ok(PreviewData {
is_base64: true,
data: general_purpose::STANDARD_NO_PAD.encode(bytes),
data_type: DataType::Sound,
})
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions bff-gui/src-tauri/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::path::Path;

use bff::names::Name;

use crate::error::BffGuiResult;
use crate::{error::BffGuiResult, PreviewData};

pub trait Export {
fn export(&self, export_path: &Path, name: Name) -> BffGuiResult<String>;
fn export(&self, export_path: &Path, name: Name) -> BffGuiResult<PreviewData>;
}
Loading

0 comments on commit 231b34b

Please sign in to comment.