Support CDDA

This commit is contained in:
Jaby 2024-05-05 18:23:17 +02:00
parent d3d8b61306
commit a092aab35c
16 changed files with 114 additions and 27 deletions

View File

@ -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>

View File

@ -1,6 +1,6 @@
[package]
name = "cdtypes"
version = "0.5.5"
version = "0.6.0"
edition = "2021"
[profile.release]

View File

@ -1,4 +1,4 @@
use super::super::{types::{error_correction::*, sector::*}};
use super::super::types::{error_correction::*, sector::*};
#[derive(Clone)]
pub enum Sector {

View File

@ -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;

View File

@ -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)
}

View File

@ -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 {

View File

@ -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"

View File

@ -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()}
}
}

View File

@ -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 {

View File

@ -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();

View File

@ -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)))

View File

@ -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(())
}
}

View File

@ -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)?)
}
}

View File

@ -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))
}

View File

@ -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)?;

View 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
},