Skip to content

Commit

Permalink
Add stdout/stdin support for unpacking blks
Browse files Browse the repository at this point in the history
  • Loading branch information
FlareFlo committed Aug 22, 2024
1 parent 79ecf83 commit 858436e
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 52 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ serde = { version = "^1.0", features = ["serde_derive"] }
const_format = { version = "0.2.31", default-features = false, features = ["fmt"] }
zip = { version = "2.1.6", features = ["deflate"], default-features = false}
wt_version = { git = "https://github.com/Warthunder-Open-Source-Foundation/wt_version.git" }
atty = "0.2.14"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand Down
20 changes: 17 additions & 3 deletions src/cli/unpack_raw_blk.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::{Arg, Command, ValueHint};
use clap::{Arg, ArgAction::SetTrue, Command, ValueHint};

pub fn unpack_raw_blk() -> Command {
Command::new("unpack_raw_blk")
Expand All @@ -10,15 +10,29 @@ pub fn unpack_raw_blk() -> Command {
.long("input_dir")
.help("Folder containing blk files, sub-folders will be recursively searched")
.required(true)
.value_hint(ValueHint::FilePath),
.value_hint(ValueHint::FilePath)
.conflicts_with("stdin"),
)
.arg(
// Not providing this argument means the input folder name will be used, with a `_u` suffix
Arg::new("Output directory")
.short('o')
.long("output_dir")
.help("Target folder that will be created to contain new files")
.value_hint(ValueHint::FilePath),
.value_hint(ValueHint::FilePath)
.conflicts_with("stdout"),
)
.arg(
Arg::new("stdout")
.long("stdout")
.help("writes to stdout instead of a file")
.action(SetTrue),
)
.arg(
Arg::new("stdin")
.long("stdin")
.help("reads from stdin instead of a file")
.action(SetTrue),
)
.arg(
Arg::new("format")
Expand Down
134 changes: 90 additions & 44 deletions src/subcommands/unpack_raw_blk.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,55 @@
use std::{
fs,
fs::OpenOptions,
io::Write,
io,
io::{Read, Write},
path::{Path, PathBuf},
str::FromStr,
};

use atty::Stream;
use clap::ArgMatches;
use color_eyre::eyre::{bail, ContextCompat, Result};
use wt_blk::{blk, blk::file::FileType};

use crate::error::CliError;

// This is the entry-point
pub fn unpack_raw_blk(args: &ArgMatches) -> Result<()> {
let input = args
.get_one::<String>("Input directory")
.ok_or(CliError::RequiredFlagMissing)?;

let format = args
.get_one::<String>("format")
.context("Invalid format specified or missing")?;

let input = Path::new(input);
if input.is_dir() {
bail!("Directories as input are not implemented yet");
}

let mut read = fs::read(input)?;
let mut input_path = None;
let mut read = get_input(&args, &mut input_path)?;

let zstd_dict = None;
let nm = None;

let bye_bye = |format| {
bail!("{format} is not implemented yet. If you need it, poke me with an issue at: https://github.com/Warthunder-Open-Source-Foundation/wt_ext_cli/issues")
};
match FileType::from_byte(read[0])? {
FileType::BBF => {},
FileType::FAT => {},
FileType::FAT_ZSTD => {},
FileType::SLIM => {
bail!("External name-map is not implemented yet");
bye_bye("External name-map")?;
},
FileType::SLIM_ZSTD => {
bail!("External name-map is not implemented yet");
bye_bye("External name-map")?;
},
FileType::SLIM_ZST_DICT => {
bail!("ZSTD dictionary is not implemented yet");
bye_bye("ZSTD dictionary")?;
},
}

let mut parsed = blk::unpack_blk(&mut read, zstd_dict, nm)?;

let mut output_folder = match () {
_ if let Some(path) = args.get_one::<String>("Output directory") => {
let buf = PathBuf::from_str(path)?;
if buf.is_absolute() {
buf
} else {
let exec_dir = std::env::current_dir()?;
exec_dir.join(buf)
}
},
_ => input.to_owned(),
};

// output_folder.push(input.file_name().unwrap().to_string_lossy().to_string());
match format.as_str() {
"Json" => {
output_folder.set_extension("json");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(output_folder)?;

parsed.merge_fields();
file.write_all(&parsed.as_serde_json()?)?;
write_output(args, parsed.as_serde_json()?, input_path, format)?;
},
"BlkText" => {
output_folder.set_extension("blkx");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(output_folder)?;
file.write_all(parsed.as_blk_text()?.as_bytes())?;
write_output(args, parsed.as_blk_text()?.into_bytes(), input_path, format)?;
},
_ => {
panic!("Unrecognized format: {format}")
Expand All @@ -89,3 +58,80 @@ pub fn unpack_raw_blk(args: &ArgMatches) -> Result<()> {

Ok(())
}

pub fn get_input(args: &ArgMatches, input_path: &mut Option<PathBuf>) -> Result<Vec<u8>> {
if let Some(p) = args.get_one::<String>("Input directory") {
let input = Path::new(p);
if input.is_dir() {
bail!("Directories as input are not implemented yet");
}
*input_path = Some(input.to_path_buf());
return Ok(fs::read(p)?);
}

if Some(&true) == args.get_one::<bool>("stdin") {
if atty::is(Stream::Stdin) {
bail!("Stdin is not connected!");
}

let mut buf = vec![];
io::stdin().read_to_end(&mut buf)?;
return Ok(buf);
}

bail!("No input passed")
}

pub fn write_output(
args: &ArgMatches,
buf: Vec<u8>,
input: Option<PathBuf>,
format: &str,
) -> Result<()> {
if args.contains_id("Output directory") {
let mut output_folder = match () {
_ if let Some(path) = args.get_one::<String>("Output directory") => {
let buf = PathBuf::from_str(path)?;
if buf.is_absolute() {
buf
} else {
let exec_dir = std::env::current_dir()?;
exec_dir.join(buf)
}
},
_ => input
.context("No output path was specified, and the input was not a file")?
.to_owned(),
};

match format {
"Json" => {
output_folder.set_extension("json");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(output_folder)?;
file.write_all(&buf)?;
},
"BlkText" => {
output_folder.set_extension("blkx");
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(output_folder)?;
file.write_all(&buf)?;
},
_ => {
panic!("Unrecognized format: {format}")
},
}
return Ok(());
}

if Some(&true) == args.get_one::<bool>("stdout") {
io::stdout().write_all(&buf)?;
return Ok(());
}

bail!("No output location passed. Pass an explicit output such as a file or stdout")
}
2 changes: 1 addition & 1 deletion src/subcommands/unpack_vromf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use color_eyre::{
use tracing::info;
use wt_blk::{
blk::util::maybe_blk,
vromf::{BlkOutputFormat, VromfUnpacker, File as BlkFile},
vromf::{BlkOutputFormat, File as BlkFile, VromfUnpacker},
};
use zip::{write::SimpleFileOptions, CompressionMethod};

Expand Down
6 changes: 2 additions & 4 deletions src/subcommands/vromf_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ pub fn vromf_version(args: &ArgMatches) -> color_eyre::Result<()> {
let parsed_input_dir = PathBuf::from_str(&input_dir).or(Err(CliError::InvalidPath))?;

let versions: Vec<_> = if parsed_input_dir.is_file() {
let unpacker = VromfUnpacker::from_file(
&File::new(parsed_input_dir.clone()).unwrap(),
true,
)?;
let unpacker =
VromfUnpacker::from_file(&File::new(parsed_input_dir.clone()).unwrap(), true)?;
vec![(
parsed_input_dir
.file_name()
Expand Down

0 comments on commit 858436e

Please sign in to comment.