Skip to content

Commit

Permalink
Add create subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
widberg committed Nov 6, 2023
1 parent b221ab6 commit 515a156
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 37 deletions.
46 changes: 46 additions & 0 deletions bff-cli/src/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::fs::File;
use std::io::BufWriter;
use std::path::{Path, PathBuf};

use bff::bigfile::BigFile;
use bff::BufReader;

use crate::error::BffCliResult;
use crate::extract::{read_names, write_names};

pub fn create(
directory: &Path,
bigfile_path: &Path,
in_names: &Vec<PathBuf>,
out_names: &Option<PathBuf>,
) -> BffCliResult<()> {
read_names(bigfile_path, in_names)?;

let manifest_path = directory.join("manifest.json");
let manifest_reader = BufReader::new(File::open(manifest_path)?);
let manifest = serde_json::from_reader(manifest_reader)?;

let mut bigfile = BigFile {
manifest,
objects: Default::default(),
};

let resources_path = directory.join("resources");
std::fs::create_dir_all(&resources_path)?;

for file in std::fs::read_dir(resources_path)? {
let path = file?.path();
if path.is_file() {
let mut file_reader = BufReader::new(File::open(&path)?);
let resource = bigfile.read_resource(&mut file_reader)?;
bigfile.objects.insert(resource.name, resource);
}
}

let mut bigfile_writer = BufWriter::new(File::create(bigfile_path)?);
bigfile.write(&mut bigfile_writer, None)?;

write_names(out_names)?;

Ok(())
}
12 changes: 10 additions & 2 deletions bff-cli/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,19 @@ pub fn extract(

let bigfile = read_bigfile(bigfile_path)?;

std::fs::create_dir(directory)?;

let manifest_path = directory.join("manifest.json");
let manifest_writer = BufWriter::new(File::create(manifest_path)?);
serde_json::to_writer_pretty(manifest_writer, &bigfile.manifest)?;

let resources_path = directory.join("resources");
std::fs::create_dir(&resources_path)?;

for resource in bigfile.objects.values() {
let name = resource.name;
let class_name = resource.class_name;
let path = directory.join(format!("{}.{}", name, class_name));
std::fs::create_dir_all(path.parent().unwrap())?;
let path = resources_path.join(format!("{}.{}", name, class_name));
let mut writer = BufWriter::new(File::create(path)?);
bigfile.dump_resource(resource, &mut writer)?;
}
Expand Down
16 changes: 16 additions & 0 deletions bff-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use reverse_crc32::DEFAULT_CHARACTER_SET;
use crate::lz::LzAlgorithm;

mod crc;
mod create;
mod csc;
mod error;
mod extract;
Expand Down Expand Up @@ -37,6 +38,15 @@ enum Commands {
#[arg(long)]
out_names: Option<PathBuf>,
},
#[clap(alias = "c")]
Create {
directory: PathBuf,
bigfile: PathBuf,
#[arg(long)]
in_names: Vec<PathBuf>,
#[arg(long)]
out_names: Option<PathBuf>,
},
#[clap(alias = "t")]
Info {
bigfile: PathBuf,
Expand Down Expand Up @@ -141,6 +151,12 @@ fn main() -> BffCliResult<()> {
in_names,
out_names,
} => extract::extract(bigfile, directory, in_names, out_names),
Commands::Create {
directory,
bigfile,
in_names,
out_names,
} => create::create(directory, bigfile, in_names, out_names),
Commands::Info { bigfile, in_names } => info::info(bigfile, in_names),
Commands::Crc {
string,
Expand Down
81 changes: 80 additions & 1 deletion bff-derive/src/bigfiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ pub fn derive_bigfiles(input: BffBigFileMacroInput) -> TokenStream {
let read_bigfile = impl_read_bigfile(&input);
let write_bigfile = impl_write_bigfile(&input);
let dump_resource = impl_dump_resource(&input);
let read_resource = impl_read_resource(&input);
let version_into_name_type = impl_version_into_name_type(&input);

quote! {
impl BigFile {
#read_bigfile
#write_bigfile
#dump_resource
#read_resource
}

#version_into_name_type
}
}

Expand Down Expand Up @@ -133,7 +138,7 @@ fn impl_dump_resource(input: &BffBigFileMacroInput) -> proc_macro2::TokenStream
.collect::<Vec<_>>();

quote! {
pub fn dump_resource<W: std::io::Write + std::io::Seek>(&self, resource: &Resource, writer: &mut W) -> crate::BffResult<()> {
pub fn dump_resource<W: std::io::Write + std::io::Seek>(&self, resource: &crate::bigfile::resource::Resource, writer: &mut W) -> crate::BffResult<()> {
use crate::versions::Version::*;
use crate::platforms::Platform::*;
use crate::traits::BigFileIo;
Expand All @@ -147,3 +152,77 @@ fn impl_dump_resource(input: &BffBigFileMacroInput) -> proc_macro2::TokenStream
}
}
}

fn impl_read_resource(input: &BffBigFileMacroInput) -> proc_macro2::TokenStream {
let arms = input
.forms
.iter()
.map(|form| {
let attrs = &form.attrs;
let pat = &form.pat;
let guard = match &form.guard {
Some((_, guard)) => quote! { #guard },
None => quote! {},
};
let body = &form.body;
quote! {
#(#attrs)*
#pat #guard => {
crate::names::names().lock().unwrap().name_type = <#body as BigFileIo>::NAME_TYPE;
Ok(<#body as BigFileIo>::ResourceType::read_resource(reader, endian)?)
}
}
})
.collect::<Vec<_>>();

quote! {
pub fn read_resource<R: std::io::Read + std::io::Seek>(&self, reader: &mut R) -> crate::BffResult<crate::bigfile::resource::Resource> {
use crate::versions::Version::*;
use crate::platforms::Platform::*;
use crate::traits::BigFileIo;
let platform = self.manifest.platform;
let endian: crate::Endian = platform.into();
let version = &self.manifest.version;
match (version.clone(), platform) {
#(#arms)*
(version, platform) => Err(crate::error::UnimplementedVersionPlatformError::new(version, platform).into()),
}
}
}
}

fn impl_version_into_name_type(input: &BffBigFileMacroInput) -> proc_macro2::TokenStream {
let arms = input
.forms
.iter()
.map(|form| {
let attrs = &form.attrs;
let pat = &form.pat;
let guard = match &form.guard {
Some((_, guard)) => quote! { #guard },
None => quote! {},
};
let body = &form.body;
quote! {
#(#attrs)*
#pat #guard => Ok(<#body as BigFileIo>::NAME_TYPE),
}
})
.collect::<Vec<_>>();

quote! {
impl TryFrom<&crate::versions::Version> for crate::names::NameType {
type Error = crate::BffError;

fn try_from(version: &crate::versions::Version) -> Result<crate::names::NameType, Self::Error> {
use crate::versions::Version::*;
use crate::platforms::Platform::*;
use crate::traits::BigFileIo;
match (version.clone(), PC) {
#(#arms)*
(version, platform) => Err(crate::error::UnimplementedVersionError::new(version).into()),
}
}
}
}
}
14 changes: 7 additions & 7 deletions bff/src/bigfile/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
use serde::Serialize;
use serde::{Deserialize, Serialize};

use crate::names::Name;
use crate::platforms::Platform;
use crate::versions::{Version, VersionXple};

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ManifestPoolObjectEntry {
pub name: Name,
pub reference_record_index: u32,
}

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ManifestPoolReferenceRecord {
pub object_entries_starting_index: u32,
pub object_entries_count: u16,
}

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ManifestPool {
pub object_entry_indices: Vec<u32>,
pub object_entries: Vec<ManifestPoolObjectEntry>,
pub reference_records: Vec<ManifestPoolReferenceRecord>,
}

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ManifestObject {
pub name: Name,
#[serde(skip_serializing_if = "Option::is_none")]
pub compress: Option<bool>,
}

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ManifestBlock {
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<u64>,
Expand All @@ -41,7 +41,7 @@ pub struct ManifestBlock {
pub objects: Vec<ManifestObject>,
}

#[derive(Serialize, Debug)]
#[derive(Serialize, Deserialize, Debug)]
pub struct Manifest {
pub version: Version,
pub platform: Platform,
Expand Down
13 changes: 1 addition & 12 deletions bff/src/bigfile/v1_06_63_02_pc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,7 @@ pub fn blocks_parser(
compress: Some(object.compress),
});

objects.insert(
object.name,
Resource {
class_name: object.class_name,
name: object.name,
compress: object.compress,
data: SplitData {
link_header: object.link_header,
body: object.body,
},
},
);
objects.insert(object.name, object.into());
}

blocks.push(ManifestBlock {
Expand Down
20 changes: 19 additions & 1 deletion bff/src/bigfile/v1_06_63_02_pc/object.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::{Seek, Write};
use std::io::{Read, Seek, Write};

use binrw::{args, binread, parser, BinRead, BinResult, BinWrite, Endian};
use derive_more::{Deref, DerefMut};
Expand Down Expand Up @@ -68,6 +68,24 @@ impl Object {
}
Ok(())
}

pub fn read_resource<R: Read + Seek>(reader: &mut R, endian: Endian) -> BinResult<Resource> {
Ok(Self::read_options(reader, endian, ())?.into())
}
}

impl From<Object> for Resource {
fn from(value: Object) -> Self {
Resource {
class_name: value.class_name,
name: value.name,
compress: value.compress,
data: SplitData {
link_header: value.link_header,
body: value.body,
},
}
}
}

#[derive(BinRead, Serialize, Debug, Deref, DerefMut)]
Expand Down
10 changes: 1 addition & 9 deletions bff/src/bigfile/v1_08_40_02_pc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,7 @@ fn blocks_parser(
compress: Some(object.compress),
});

objects.insert(
object.name,
Resource {
class_name: object.class_name,
name: object.name,
compress: object.compress,
data: Data(object.data),
},
);
objects.insert(object.name, object.into());
}

blocks.push(ManifestBlock {
Expand Down
19 changes: 17 additions & 2 deletions bff/src/bigfile/v1_08_40_02_pc/object.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io::{Seek, Write};
use std::io::{Read, Seek, Write};

use binrw::{binread, BinResult, BinWrite, Endian};
use binrw::{binread, BinRead, BinResult, BinWrite, Endian};
use serde::Serialize;

use crate::bigfile::resource::Resource;
Expand Down Expand Up @@ -50,4 +50,19 @@ impl Object {

Ok(())
}

pub fn read_resource<R: Read + Seek>(reader: &mut R, endian: Endian) -> BinResult<Resource> {
Ok(Self::read_options(reader, endian, ())?.into())
}
}

impl From<Object> for Resource {
fn from(value: Object) -> Self {
Resource {
class_name: value.class_name,
name: value.name,
compress: value.compress,
data: Data(value.data),
}
}
}
7 changes: 7 additions & 0 deletions bff/src/bigfile/v1_22_pc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ impl Resource {

Ok(())
}

pub fn read_resource<R: Read + Seek>(
reader: &mut R,
endian: Endian,
) -> BinResult<crate::bigfile::resource::Resource> {
Ok(Self::read_options(reader, endian, ())?.into())
}
}

impl From<Resource> for crate::bigfile::resource::Resource {
Expand Down
7 changes: 7 additions & 0 deletions bff/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ pub struct UnimplementedVersionPlatformError {
pub platform: Platform,
}

#[derive(Debug, Constructor, Display, Error)]
#[display(fmt = "Unsupported BigFile version: {}", version)]
pub struct UnimplementedVersionError {
pub version: Version,
}

#[derive(Debug, Constructor, Display, Error)]
#[display(fmt = "Invalid BigFile extension {:#?}", extension)]
pub struct InvalidExtensionError {
Expand All @@ -53,6 +59,7 @@ pub enum Error {
Io(std::io::Error),
ParseInt(std::num::ParseIntError),
UnimplementedClass(UnimplementedClassError),
UnimplementedVersion(UnimplementedVersionError),
UnimplementedVersionPlatform(UnimplementedVersionPlatformError),
Utf8(std::string::FromUtf8Error),
}
Loading

0 comments on commit 515a156

Please sign in to comment.