Integrate all the progress into master #6
|
@ -31,6 +31,6 @@
|
|||
<File name = "TEX.BIN" lz4 = "already">../assets/bin/TexturePage.bin</File>
|
||||
<File name = "ICON.BIN" lz4 = "already">../assets/bin/IconTexture.bin</File>
|
||||
</Directory>
|
||||
|
||||
</Track>
|
||||
<CD_Audio>../assets/audio/Evacuation_cdda.wav</CD_Audio>
|
||||
</ISO_Project>
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "cdtypes"
|
||||
version = "0.5.5"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::super::{types::{error_correction::*, sector::*}};
|
||||
use super::super::types::{error_correction::*, sector::*};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Sector {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{date::SmallDate, helper::{force_convert_ascii_to_str}, lsb_msb::{ReadWriteEndian, BigEndianU16, LittleBigEndianU32, LittleBigEndianU16}};
|
||||
use super::{date::SmallDate, helper::force_convert_ascii_to_str, lsb_msb::{ReadWriteEndian, BigEndianU16, LittleBigEndianU32, LittleBigEndianU16}};
|
||||
use crate::read_write_bit_getter_setter;
|
||||
use std::concat;
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@ pub fn force_convert_ascii_to_str(bytes: &[u8]) -> &str {
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn sector_count_audio(audio_samples: usize) -> usize {
|
||||
multiple_of_round_up(audio_samples, sector::Audio::SAMPLE_SIZE)
|
||||
}
|
||||
|
||||
pub const fn sector_count_mode2_form1(data_size: usize) -> usize {
|
||||
multiple_of_round_up(data_size, sector::Mode2Form1::DATA_SIZE)
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ impl std::default::Default for AudioSample {
|
|||
#[repr(packed(1))]
|
||||
#[derive(Clone)]
|
||||
pub struct Audio {
|
||||
samples: [AudioSample; Self::SAMPLE_SIZE]
|
||||
pub samples: [AudioSample; Self::SAMPLE_SIZE]
|
||||
}
|
||||
|
||||
impl Audio {
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
[package]
|
||||
name = "psxcdgen_ex"
|
||||
version = "0.2.3"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
cdtypes = {path = "../cdtypes"}
|
||||
clap = {version = "4.4.11", features = ["derive"]}
|
||||
colored = "2.1.0"
|
||||
no-comment = "0.0.3"
|
||||
paste = "1.0.14"
|
||||
roxmltree = "0.19.0"
|
||||
tool_helper = {path = "../tool_helper"}
|
||||
cdtypes = {path = "../cdtypes"}
|
||||
clap = {version = "4.4.11", features = ["derive"]}
|
||||
colored = "2.1.0"
|
||||
no-comment = "0.0.3"
|
||||
paste = "1.0.14"
|
||||
roxmltree = "0.19.0"
|
||||
tool_helper = {path = "../tool_helper"}
|
||||
wav = "1.0.0"
|
||||
|
|
|
@ -9,14 +9,15 @@ pub enum LZ4State {
|
|||
}
|
||||
|
||||
pub struct Configuration {
|
||||
pub publisher: Option<String>,
|
||||
pub license_path: Option<PathBuf>,
|
||||
pub root: Directory,
|
||||
pub publisher: Option<String>,
|
||||
pub license_path: Option<PathBuf>,
|
||||
pub root: Directory,
|
||||
pub cd_audio_files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
pub fn new() -> Configuration {
|
||||
Configuration{publisher: None, license_path: None, root: Directory::new("root", false)}
|
||||
Configuration{publisher: None, license_path: None, root: Directory::new("root", false), cd_audio_files: Vec::new()}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ fn parse_iso_project(iso_project: roxmltree::Node, config: &mut Configuration) -
|
|||
match node.tag_name().name() {
|
||||
"Description" => parse_description(node, config),
|
||||
"Track" => parse_track(node, config),
|
||||
"CD_Audio" => parse_cd_audio(node, config),
|
||||
_ => Ok(())
|
||||
}?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,11 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(),
|
|||
parse_file_system(track, &mut config.root, false)
|
||||
}
|
||||
|
||||
fn parse_cd_audio(cdda: roxmltree::Node, config: &mut Configuration) -> Result<(), Error> {
|
||||
config.cd_audio_files.push(path_from_node(&cdda, "CD Audio file")?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_common_properties(xml: &roxmltree::Node, is_hidden: bool, force_lz4_state: Option<LZ4State>) -> Result<CommonProperties, Error> {
|
||||
let lz4_state = {
|
||||
if let Some(forced_lz4_state) = force_lz4_state {
|
||||
|
|
|
@ -24,6 +24,20 @@ impl SubModeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_audio_for_vec(audio_samples: &Vec<AudioSample>) -> Vec<Audio> {
|
||||
let samples_to_parse = sector_count_audio(audio_samples.len());
|
||||
let mut samples = &audio_samples[0..audio_samples.len()];
|
||||
let mut parsed_samples = Vec::new();
|
||||
|
||||
for _ in 0..samples_to_parse {
|
||||
let mut audio = Audio::new();
|
||||
|
||||
samples = copy_array(&mut audio.samples, samples);
|
||||
parsed_samples.push(audio);
|
||||
}
|
||||
parsed_samples
|
||||
}
|
||||
|
||||
pub fn create_xa_data_for_raw(mut sub_mode: SubMode, data: &[u8; Mode2Form1::DATA_SIZE]) -> Mode2Form1 {
|
||||
let mut sector = Mode2Form1::new();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::{*, SectorWriter, {CDDesc, Error}};
|
||||
use super::super::types::{helper::{DirectoryRecordMember, PathTableMember}, layout::Layout, *};
|
||||
use builder::SubModeBuilder;
|
||||
use cdtypes::types::{cdstring::{AString, DString}, date::*, dir_record::*, helper::{round_bytes_mode2_form1, sector_count_mode2_form1}, path_table::*, pvd as cd_pvd, lsb_msb::*, sector::Mode2Form1};
|
||||
use cdtypes::types::{cdstring::{AString, DString}, date::*, dir_record::*, helper::{round_bytes_mode2_form1, sector_count_mode2_form1}, lsb_msb::*, path_table::*, pvd as cd_pvd, sector::{AudioSample, Mode2Form1}};
|
||||
use tool_helper::{BufferedInputFile, format_if_error, open_input_file_buffered, print_warning};
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
|
@ -108,7 +108,8 @@ pub fn encode_psx_image(cd_desc: &CDDesc, sec_writer: &mut dyn SectorWriter) ->
|
|||
}
|
||||
}
|
||||
|
||||
process_end_dummy_section(cd_desc.end_dummy_padding, sec_writer)
|
||||
process_end_dummy_section(cd_desc.end_dummy_padding, sec_writer)?;
|
||||
process_cd_da(&cd_desc.cd_da_tracks, sec_writer)
|
||||
}
|
||||
|
||||
fn process_system_area(system_area: &SystemArea, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||
|
@ -361,6 +362,16 @@ fn process_end_dummy_section(padding: usize, sec_writer: &mut dyn SectorWriter)
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn process_cd_da(cd_da_tracks: &Vec<Vec<AudioSample>>, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||
for cd_da_track in cd_da_tracks {
|
||||
sec_writer.cd_da_start()?;
|
||||
for audio in builder::create_audio_for_vec(cd_da_track) {
|
||||
sec_writer.write_audio(audio)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_path_table(path_table: &PathTable) -> Result<(), Error> {
|
||||
if path_table.size_bytes > Mode2Form1::DATA_SIZE {
|
||||
Err(Error::from_text(format!("Path Tables are not allowed to be bigger then {} bytes - Path Table has {} bytes", Mode2Form1::DATA_SIZE, path_table.size_bytes)))
|
||||
|
|
|
@ -6,12 +6,13 @@ use tool_helper::get_file_name_from_path_buf;
|
|||
pub struct BinCueWriter {
|
||||
bin_out: std::boxed::Box<dyn Write>,
|
||||
cd_time: Time,
|
||||
track: u64,
|
||||
cue: Vec<CueSpecifier>,
|
||||
}
|
||||
|
||||
impl BinCueWriter {
|
||||
pub fn new<T: Write + 'static>(writer: T, bin_path: &PathBuf) -> Result<BinCueWriter, Error> {
|
||||
Ok(BinCueWriter{bin_out: std::boxed::Box::new(writer), cd_time: Time::cd_start(), cue: Self::default_cue(bin_path)?})
|
||||
Ok(BinCueWriter{bin_out: std::boxed::Box::new(writer), cd_time: Time::cd_start(), track: 1, cue: Self::default_cue(bin_path)?})
|
||||
}
|
||||
|
||||
pub fn write_cue<T: Write>(self, dst: T) -> Result<(), Error> {
|
||||
|
@ -45,4 +46,12 @@ impl SectorWriter for BinCueWriter {
|
|||
|
||||
Ok(self.bin_out.write(sector.as_raw())?)
|
||||
}
|
||||
|
||||
fn cd_da_start(&mut self) -> Result<(), Error> {
|
||||
self.track += 1;
|
||||
self.cue.push(CueSpecifier::Track{number: self.track, data_type: CueDataType::Audio});
|
||||
// Skip pregap because why have one?
|
||||
self.cue.push(CueSpecifier::Index{number: 1, time: self.cd_time.clone()});
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ pub trait SectorWriter {
|
|||
}
|
||||
|
||||
fn write(&mut self, sector: Sector) -> Result<usize, Error>;
|
||||
fn cd_da_start(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub fn write_image(cd_desc: &CDDesc, encoder: ImageEncoderFunction, image_type: ImageType, mut output_path: PathBuf) -> Result<(), Error> {
|
||||
|
@ -52,6 +53,7 @@ pub fn write_image(cd_desc: &CDDesc, encoder: ImageEncoderFunction, image_type:
|
|||
let mut writer = BinCueWriter::new(open_output_file(&output_path)?, &output_path)?;
|
||||
|
||||
encoder(cd_desc, &mut writer)?;
|
||||
// TODO: Write times here?!
|
||||
writer.write_cue(open_output_file(&cue_output_path)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod encoder;
|
|||
pub mod file_writer;
|
||||
pub mod types;
|
||||
|
||||
use cdtypes::types::sector::AudioSample;
|
||||
use config_reader::LZ4State;
|
||||
use encoder::{LbaCalculatorFunction, LengthCalculatorFunction};
|
||||
use tool_helper::{format_if_error, Output, read_file};
|
||||
|
@ -193,11 +194,48 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc,
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let cd_desc = CDDesc::new();
|
||||
fn parse_cd_da(cd_da_tracks: &mut Vec<Vec<AudioSample>>, cd_da_files: Vec<PathBuf>) -> Result<(), Error> {
|
||||
fn convert_into_16(data: wav::bit_depth::BitDepth, file_path: &PathBuf) -> Result<Vec<AudioSample>, Error> {
|
||||
match data.try_into_sixteen() {
|
||||
Ok(data) => {
|
||||
let mut samples = Vec::new();
|
||||
|
||||
for (left, right) in data.iter().step_by(2).zip(data.iter().skip(1).step_by(2)) {
|
||||
samples.push(AudioSample::new(*left, *right))
|
||||
}
|
||||
|
||||
Ok(samples)
|
||||
},
|
||||
Err(_) => Err(Error::from_text(format!("{}: Couldn't convert samples to 16bit", file_path.display()))),
|
||||
}
|
||||
}
|
||||
|
||||
for cd_da_file in cd_da_files {
|
||||
let mut audio_io = {
|
||||
match std::fs::File::open(&cd_da_file) {
|
||||
Ok(io) => Ok(io),
|
||||
Err(error) => Err(Error::from_text(format!("Opening CD Audio file \"{}\" failed with: {}", cd_da_file.display(), error)))
|
||||
}
|
||||
}?;
|
||||
let (header, raw) = wav::read(&mut audio_io)?;
|
||||
|
||||
if header.audio_format != wav::header::WAV_FORMAT_PCM {
|
||||
return Err(Error::from_text(format!("{}: Only WAV PCM is supported for Audio tracks", cd_da_file.display())));
|
||||
}
|
||||
|
||||
if header.sampling_rate != 44_100 {
|
||||
return Err(Error::from_text(format!("{}: Only a sampling rate of 44.1kHz is supported for Audio tracks", cd_da_file.display())));
|
||||
}
|
||||
|
||||
cd_da_tracks.push(convert_into_16(raw, &cd_da_file)?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let mut cd_desc = CDDesc::new();
|
||||
let mut lba_embedded_files = Vec::new();
|
||||
|
||||
if let Some(publisher) = config.publisher {
|
||||
|
@ -209,5 +247,6 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc,
|
|||
}
|
||||
|
||||
parse_dir(&mut cd_desc.root.borrow_mut(), config.root, &mut lba_embedded_files)?;
|
||||
parse_cd_da(&mut cd_desc.cd_da_tracks, config.cd_audio_files)?;
|
||||
Ok((cd_desc, lba_embedded_files))
|
||||
}
|
||||
|
|
|
@ -45,10 +45,9 @@ fn run_main(cmd_line: CommandLine) -> Result<(), Error> {
|
|||
let content_size = psxcdgen_ex::process_files(file_map, lba_embedded_files, encoding_functions.length_calculator)?;
|
||||
if content_size < PKG_MINIMUM_CONTENT_SIZE {
|
||||
let missing_size = PKG_MINIMUM_CONTENT_SIZE - content_size;
|
||||
|
||||
// TODO: How does this matter when we have CD tracks?
|
||||
desc.set_end_padding(missing_size);
|
||||
tool_helper::print_warning(format!("Content size {}b smaller then {}b.\nCD will be padded with {}b to work as a .pkg", content_size, PKG_MINIMUM_CONTENT_SIZE, missing_size));
|
||||
|
||||
}
|
||||
|
||||
write_image(&desc, encoding_functions.encoder, cmd_line.output_type, cmd_line.output_file)?;
|
||||
|
|
|
@ -3,7 +3,7 @@ pub mod layout;
|
|||
pub mod file_map;
|
||||
pub mod overlay;
|
||||
|
||||
use cdtypes::types::{cdstring::DString, dir_record::DirectoryRecord, path_table::PathTableL};
|
||||
use cdtypes::types::{cdstring::DString, dir_record::DirectoryRecord, path_table::PathTableL, sector::AudioSample};
|
||||
use std::{cell::RefCell, path::PathBuf, rc::Rc};
|
||||
|
||||
pub use file_map::FileSystemMap;
|
||||
|
@ -19,6 +19,7 @@ pub struct CDDesc {
|
|||
pub(super) path_table: SharedPtr<PathTable>,
|
||||
pub(super) pvd: SharedPtr<PrimaryVolumeDescriptor>,
|
||||
pub(super) root: SharedPtr<Directory>,
|
||||
pub(super) cd_da_tracks: Vec<Vec<AudioSample>>,
|
||||
pub(super) vol_sector_count: usize,
|
||||
pub(super) end_dummy_padding: usize
|
||||
}
|
||||
|
@ -30,7 +31,8 @@ impl CDDesc {
|
|||
system_area: new_shared_ptr(SystemArea::new()),
|
||||
path_table: new_shared_ptr(PathTable::new()),
|
||||
pvd: new_shared_ptr(PrimaryVolumeDescriptor::new()),
|
||||
root: new_shared_ptr(root),
|
||||
root: new_shared_ptr(root),
|
||||
cd_da_tracks: Vec::new(),
|
||||
vol_sector_count: 0,
|
||||
end_dummy_padding: 0
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue