Basic project suport

This commit is contained in:
Jaby 2025-04-14 20:47:56 +02:00
parent e3ed4a3b43
commit 4ea7575057
7 changed files with 5872 additions and 85 deletions

File diff suppressed because it is too large Load Diff

View File

@ -34,5 +34,8 @@ hound = "3.5.1"
paste = "1.0.15" paste = "1.0.15"
png = "0.17.16" png = "0.17.16"
rubato = "0.16.1" rubato = "0.16.1"
serde = {version = "1.0.219", features = ["derive"]}
serde_json = "1.0"
symphonia = {version = "0.5.4", optional = true, default-features = false} symphonia = {version = "0.5.4", optional = true, default-features = false}
tim_tool = {path = "../tim_tool"}
tool_helper = {path = "../tool_helper"} tool_helper = {path = "../tool_helper"}

View File

@ -9,13 +9,13 @@ use tool_helper::{Error, Input};
#[derive(Args)] #[derive(Args)]
pub struct Arguments { pub struct Arguments {
#[clap(flatten)] #[clap(flatten)]
global: super::args::Arguments, pub(crate) global: super::args::Arguments,
#[clap(long, value_parser, default_value_t, value_name = Point::POINT_VALUE_NAME)] #[clap(long, value_parser, default_value_t, value_name = Point::POINT_VALUE_NAME)]
clut_pos: Point, pub(crate) clut_pos: Point,
#[clap(long, value_parser, default_value_t, value_name = Point::POINT_VALUE_NAME)] #[clap(long, value_parser, default_value_t, value_name = Point::POINT_VALUE_NAME)]
tex_pos: Point, pub(crate) tex_pos: Point,
} }
pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> { pub fn convert(args: Arguments, input: Input, output: &mut dyn Write) -> Result<(), Error> {

View File

@ -1,3 +1,67 @@
pub mod audio; pub mod audio;
pub mod images; pub mod images;
pub mod nothing; pub mod nothing;
pub mod project;
use crate::audio::*;
use crate::images::*;
use clap::Subcommand;
use std::path::PathBuf;
use tool_helper::Error;
#[derive(Subcommand)]
pub enum SubCommands {
Nothing,
SimpleTIM(reduced_tim::Arguments),
TIM(tim::Arguments),
VAG(vag::Arguments),
XA(xa::Arguments),
Project,
}
pub fn run_subcommand(compress_lz4: bool, input_path: Option<PathBuf>, output_path: Option<PathBuf>, sub_command: SubCommands) -> Result<(), Error> {
let mut input = tool_helper::open_input(&input_path)?;
let mut buffer = Vec::<u8>::new();
let mut output_file = tool_helper::open_output(&output_path)?;
let dst_buffer = {
if compress_lz4 {
&mut buffer as &mut dyn std::io::Write
}
else {
&mut output_file as &mut dyn std::io::Write
}
};
let cmd_result: Result<(), Error> = {
match sub_command {
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
SubCommands::TIM(args) => tim::convert(args, input, dst_buffer),
SubCommands::VAG(args) => audio::vag::convert(args, &output_path, input, dst_buffer),
SubCommands::XA(args) => audio::xa::convert(args, input, dst_buffer),
SubCommands::Project => project::run_project(input, input_path, &output_path),
}
};
if let Err(cmd_error) = cmd_result {
if let Some(file_path) = output_path {
let _result = std::fs::remove_file(file_path);
}
else {
tool_helper::print_warning("Open stream detected! Incomplete file can not be deleted".to_owned());
}
return Err(cmd_error);
}
// We encoded the file to a temporary buffer and now need to write it
if compress_lz4 {
let buffer = tool_helper::compress::psx_default::lz4(&buffer)?;
output_file.write(&buffer)?;
}
Ok(())
}

View File

@ -1,11 +1,11 @@
use clap::{Parser, Subcommand}; use clap::Parser;
use psxfileconv::{audio::{self, *}, images::*, nothing};
use std::path::PathBuf; use std::path::PathBuf;
use tool_helper::{exit_with_error, Error}; use psxfileconv::{SubCommands, run_subcommand};
use tool_helper::exit_with_error;
#[derive(Parser)] #[derive(Parser)]
#[clap(about = "Converts files to various JabyEngine related file formats", long_about = None)] #[clap(about = "Converts files to various JabyEngine related file formats", long_about = None)]
struct CommandLine { pub struct CommandLine {
#[clap(long="lz4", default_value_t=false)] #[clap(long="lz4", default_value_t=false)]
compress_lz4: bool, compress_lz4: bool,
@ -19,64 +19,10 @@ struct CommandLine {
sub_command: SubCommands, sub_command: SubCommands,
} }
#[derive(Subcommand)]
enum SubCommands {
Nothing,
SimpleTIM(reduced_tim::Arguments),
TIM(tim::Arguments),
VAG(vag::Arguments),
XA(xa::Arguments),
}
fn run_main(cmd: CommandLine) -> Result<(), Error> {
let mut input = tool_helper::open_input(&cmd.input_file)?;
let mut buffer = Vec::<u8>::new();
let mut output_file = tool_helper::open_output(&cmd.output_file)?;
let dst_buffer = {
if cmd.compress_lz4 {
&mut buffer as &mut dyn std::io::Write
}
else {
&mut output_file as &mut dyn std::io::Write
}
};
let cmd_result: Result<(), Error> = {
match cmd.sub_command {
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
SubCommands::TIM(args) => tim::convert(args, input, dst_buffer),
SubCommands::VAG(args) => audio::vag::convert(args, &cmd.output_file, input, dst_buffer),
SubCommands::XA(args) => audio::xa::convert(args, input, dst_buffer),
}
};
if let Err(cmd_error) = cmd_result {
if let Some(file_path) = cmd.output_file {
let _result = std::fs::remove_file(file_path);
}
else {
tool_helper::print_warning("Open stream detected! Incomplete file can not be deleted".to_owned());
}
return Err(cmd_error);
}
// We encoded the file to a temporary buffer and now need to write it
if cmd.compress_lz4 {
let buffer = tool_helper::compress::psx_default::lz4(&buffer)?;
output_file.write(&buffer)?;
}
Ok(())
}
fn main() { fn main() {
match CommandLine::try_parse() { match CommandLine::try_parse() {
Ok(cmd) => { Ok(cmd) => {
if let Err(error) = run_main(cmd) { if let Err(error) = run_subcommand(cmd.compress_lz4, cmd.input_file, cmd.output_file, cmd.sub_command) {
exit_with_error(error); exit_with_error(error);
} }
}, },

View File

@ -0,0 +1,77 @@
use crate::images::args::{Arguments, ClutAlignment, ColorType};
use std::path::PathBuf;
use tool_helper::{Error, Input};
use tim_tool::logic::{tim::types::Encoding, project::*};
pub fn run_project(input: Input, input_path: Option<PathBuf>, output_path: &Option<PathBuf>) -> Result<(), Error> {
let location_path = if let Some(input_path) = input_path {input_path} else {PathBuf::from(".")};
let output_path = if let Some(output_path) = &output_path {
let mut output_path = output_path.clone();
if output_path.is_file() {
output_path.pop();
}
output_path
} else {PathBuf::from(".")};
let project = serde_json::from_reader::<Input, Project>(input).map_err(|error|{Error::from_text(format!("Reading project failed: {}", error))})?;
for job in project.jobs {
let args = to_arguments(&job)?;
let output_path = {
let mut path = output_path.clone();
path.push(job.name);
path.set_extension("img");
path
};
let sub_command = crate::SubCommands::SimpleTIM(args);
crate::run_subcommand(job.settings.compress, Some(Job::create_file_path(job.file_path, &location_path)), Some(output_path), sub_command)?;
// Create .cpp file
}
Ok(())
}
fn to_arguments(job: &Job) -> Result<Arguments, Error> {
let (semi_transparent, transparent_palette) = get_transparency(&job.settings.transparency);
return Ok(Arguments {
color_depth: get_encoding(&job.settings.encoding),
clut_align: get_palette_rect(&job.palette_rect)?,
semi_transparent,
transparent_palette,
});
fn get_transparency(transparency: &Transparency) -> (bool, bool) {
match transparency {
Transparency::None => (false, false),
Transparency::FirstColor => (false, true),
Transparency::PSXSemi => (true, false),
Transparency::Both => (true, true),
}
}
fn get_encoding(encoding: &Encoding) -> ColorType {
match encoding {
Encoding::FourBit => ColorType::Clut4,
Encoding::EightBit => ColorType::Clut8,
Encoding::FullColor => ColorType::Full16,
}
}
fn get_palette_rect(rect: &Option<PaletteRect>) -> Result<ClutAlignment, Error> {
if let Some(rect) = rect {
if rect.size.height == 1 {
return Ok(ClutAlignment::Linear);
}
if rect.size.width%16 == 0 {
return Ok(ClutAlignment::Block);
}
return Err(Error::from_text(format!("psxfileconv currently only supports linear or block CLUTs with a width of 16px")));
}
Ok(ClutAlignment::None)
}
}