From 4b56c5c5d43d1dc817c121b40cc8e0efe500863d Mon Sep 17 00:00:00 2001 From: Jaby Date: Wed, 22 May 2024 20:22:07 +0200 Subject: [PATCH] Prepare XA-Audio interleaving --- src/Tools/cdtypes/src/types/helper.rs | 9 ++++ .../psxcdgen_ex/src/config_reader/mod.rs | 3 +- .../psxcdgen_ex/src/config_reader/xml.rs | 19 +++++-- src/Tools/psxcdgen_ex/src/encoder/builder.rs | 3 +- src/Tools/psxcdgen_ex/src/encoder/psx.rs | 49 ++++++++++--------- src/Tools/psxcdgen_ex/src/lib.rs | 13 ++++- src/Tools/psxcdgen_ex/src/types/mod.rs | 32 +++++++++--- .../psxcdgen_ex/src/types/overlay/main.rs | 4 +- .../psxcdgen_ex/src/types/overlay/mod.rs | 9 ++-- 9 files changed, 100 insertions(+), 41 deletions(-) diff --git a/src/Tools/cdtypes/src/types/helper.rs b/src/Tools/cdtypes/src/types/helper.rs index f3f667c0..d55c242d 100644 --- a/src/Tools/cdtypes/src/types/helper.rs +++ b/src/Tools/cdtypes/src/types/helper.rs @@ -39,6 +39,15 @@ pub fn force_convert_ascii_to_str(bytes: &[u8]) -> &str { } } +pub const fn xa_audio_interleave_count(files: usize) -> usize { + match files { + 0..=4 => 4, + 5..=8 => 8, + 9..=16 => 16, + _ => 32 + } +} + pub const fn sector_count_audio(audio_samples: usize) -> usize { multiple_of_round_up(audio_samples, sector::Audio::SAMPLE_SIZE) } diff --git a/src/Tools/psxcdgen_ex/src/config_reader/mod.rs b/src/Tools/psxcdgen_ex/src/config_reader/mod.rs index 380ec0b8..406111d5 100644 --- a/src/Tools/psxcdgen_ex/src/config_reader/mod.rs +++ b/src/Tools/psxcdgen_ex/src/config_reader/mod.rs @@ -31,7 +31,8 @@ pub struct CommonProperties { pub enum FileKind { Regular, Main(PathBuf), - Overlay(PathBuf) + Overlay(PathBuf), + XA_Audio(Vec), } pub struct File { diff --git a/src/Tools/psxcdgen_ex/src/config_reader/xml.rs b/src/Tools/psxcdgen_ex/src/config_reader/xml.rs index 4c0787d8..65ca1e5a 100644 --- a/src/Tools/psxcdgen_ex/src/config_reader/xml.rs +++ b/src/Tools/psxcdgen_ex/src/config_reader/xml.rs @@ -78,15 +78,28 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), } fn parse_overlay_file(file: roxmltree::Node, is_hidden: bool) -> Result { - // v they will be compressed automatically + // v They will be compressed automatically let common = read_common_properties(&file, is_hidden, Some(LZ4State::AlreadyCompressed))?; let path = path_from_node(&file, &common.name)?; Ok(File{common, path, kind: FileKind::Overlay(PathBuf::from(file.attribute(attribute_names::LBA_SOURCE).unwrap_or_default()))}) } - fn parse_xa_audio(_file: roxmltree::Node, _is_hidden: bool) -> Result { - Err(Error::not_implemented("XA-Audio not supported yet")) + fn parse_xa_audio(file: roxmltree::Node, is_hidden: bool) -> Result { + // v Never compress XA-Audio + let common = read_common_properties(&file, is_hidden, Some(LZ4State::None))?; + let channel = { + let mut channel = Vec::new(); + for node in file.children() { + if node.is_element() && node.tag_name().name() == "Channel" { + channel.push(path_from_node(&node, "Channel")?); + } + } + + channel + }; + + Ok(File{common, path: PathBuf::new(), kind: FileKind::XA_Audio(channel)}) } fn parse_file_system(cur_node: roxmltree::Node, root: &mut Directory, mut is_hidden: bool) -> Result<(), Error> { diff --git a/src/Tools/psxcdgen_ex/src/encoder/builder.rs b/src/Tools/psxcdgen_ex/src/encoder/builder.rs index bbe6bcc2..a0c1859d 100644 --- a/src/Tools/psxcdgen_ex/src/encoder/builder.rs +++ b/src/Tools/psxcdgen_ex/src/encoder/builder.rs @@ -1,3 +1,4 @@ +use crate::types::RawData; use cdtypes::types::{helper::*, sector::*}; pub struct SubModeBuilder { @@ -62,7 +63,7 @@ pub fn create_xa_data_for(sub_mode: SubMode, data: &T) -> Mode2Form1 { create_xa_data_for_raw(sub_mode, unsafe {std::mem::transmute::<&T, &[u8; Mode2Form1::DATA_SIZE]>(data)}) } -pub fn create_xa_data_for_vec(sub_mode: Option, data: &Vec) -> Vec { +pub fn create_xa_data_for_vec(sub_mode: Option, data: &RawData) -> Vec { let sectors_to_parse = sector_count_mode2_form1(data.len()); let mut data = &data[0..data.len()]; let mut sectors = vec![Mode2Form1::new(); sectors_to_parse]; diff --git a/src/Tools/psxcdgen_ex/src/encoder/psx.rs b/src/Tools/psxcdgen_ex/src/encoder/psx.rs index fec3aaf6..e881f637 100644 --- a/src/Tools/psxcdgen_ex/src/encoder/psx.rs +++ b/src/Tools/psxcdgen_ex/src/encoder/psx.rs @@ -325,33 +325,38 @@ fn process_directory_record(dir: &Directory, sec_writer: &mut dyn SectorWriter) } fn process_file(file: &File, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> { + fn process_regular_file(content: &RawData, sec_writer: &mut dyn SectorWriter, padded_size: usize) -> Result<(), Error> { + let content_sectors = builder::create_xa_data_for_vec(None, content); + let content_sector_count = content_sectors.len(); + + for sector in content_sectors { + sec_writer.write_cd_xa_data(sector)?; + } + + let extended_sector_count = sector_count_mode2_form1(padded_size) - content_sector_count; + for _ in 0..extended_sector_count { + sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?; + } + + Ok(()) + } + + fn process_cd_xa_file(_content: &Vec, _sec_writer: &mut dyn SectorWriter) -> Result<(), Error> { + Err(Error::not_implemented("Encoding XA Audio")) + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if !file.properties.is_size_valid() { return Err(create_wrong_padding_error("File", file.name.to_string(), file.properties.get_padded_size(), file.properties.get_real_size())); } - let content_sectors = { - match &file.content { - FileType::Regular(raw) => builder::create_xa_data_for_vec(None, raw), - FileType::Main(_, _) => { - return Err(Error::from_str("Trying to encode an unprocssed main file")); - }, - FileType::Overlay(_, _) => { - return Err(Error::from_str("Trying to encode an unprocessed overlay file")); - }, - } - }; - let content_sector_count = content_sectors.len(); - - for sector in content_sectors { - sec_writer.write_cd_xa_data(sector)?; + match &file.content { + FileType::Regular(raw) => process_regular_file(raw, sec_writer, file.properties.get_padded_size()), + FileType::XA_Audio(raw) => process_cd_xa_file(raw, sec_writer), + FileType::Main(_, _) => Err(Error::from_str("Trying to encode an unprocssed main file")), + FileType::Overlay(_, _) => Err(Error::from_str("Trying to encode an unprocessed overlay file")), } - - let extended_sector_count = sector_count_mode2_form1(file.properties.get_padded_size()) - content_sector_count; - for _ in 0..extended_sector_count { - sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?; - } - - Ok(()) } fn process_end_dummy_section(padding: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> { diff --git a/src/Tools/psxcdgen_ex/src/lib.rs b/src/Tools/psxcdgen_ex/src/lib.rs index 708e02a8..79e89eb2 100644 --- a/src/Tools/psxcdgen_ex/src/lib.rs +++ b/src/Tools/psxcdgen_ex/src/lib.rs @@ -5,6 +5,7 @@ pub mod encoder; pub mod file_writer; pub mod types; +use crate::types::RawData; use cdtypes::types::sector::AudioSample; use config_reader::LZ4State; use encoder::{LbaCalculatorFunction, LengthCalculatorFunction}; @@ -161,7 +162,7 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc, config_reader::DirMember::File(file) => { let lz4_state = file.common.lz4_state; let (mut desc_file, needs_treatment) = { - fn handle_file_load(file_path: &PathBuf, lz4_state: &LZ4State) -> Result, Error> { + fn handle_file_load(file_path: &PathBuf, lz4_state: &LZ4State) -> Result { let file_content = read_file(file_path)?; if matches!(lz4_state, LZ4State::Compress) { @@ -173,10 +174,20 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc, } } + fn handle_multiple_file_load(file_paths: Vec, lz4_state: &LZ4State) -> Result, Error> { + let mut files = Vec::new(); + + for file_path in file_paths { + files.push(handle_file_load(&file_path, &lz4_state)?); + } + Ok(files) + } + match file.kind { config_reader::FileKind::Regular => (types::File::new_regular(file.common.name.as_str(), handle_file_load(&file.path, &lz4_state)?)?, false), config_reader::FileKind::Main(lba_source) => (types::overlay::load_for_main(file.common.name.as_str(), handle_file_load(&file.path, &lz4_state)?, lba_source)?, true), config_reader::FileKind::Overlay(lba_source) => (types::overlay::load_from(file.common.name.as_str(), &file.path, lba_source)?, true), + config_reader::FileKind::XA_Audio(channels) => (types::File::new_xa_audio(file.common.name.as_str(), handle_multiple_file_load(channels, &lz4_state)?)?, false), } }; diff --git a/src/Tools/psxcdgen_ex/src/types/mod.rs b/src/Tools/psxcdgen_ex/src/types/mod.rs index 5a911ae1..281392a1 100644 --- a/src/Tools/psxcdgen_ex/src/types/mod.rs +++ b/src/Tools/psxcdgen_ex/src/types/mod.rs @@ -3,12 +3,13 @@ pub mod layout; pub mod file_map; pub mod overlay; -use cdtypes::types::{cdstring::DString, dir_record::DirectoryRecord, path_table::PathTableL, sector::AudioSample}; +use cdtypes::types::{cdstring::DString, dir_record::DirectoryRecord, helper::xa_audio_interleave_count, path_table::PathTableL, sector::AudioSample}; use std::{cell::RefCell, path::PathBuf, rc::Rc}; pub use file_map::FileSystemMap; pub use tool_helper::Error; pub type SharedPtr = Rc>; +pub type RawData = Vec; pub fn new_shared_ptr(value: T) -> SharedPtr { Rc::new(RefCell::new(value)) @@ -216,9 +217,10 @@ impl std::fmt::Display for Directory { } pub(super) enum FileType { - Regular(Vec), - Main(Vec, overlay::LBANameVec), - Overlay(Vec, overlay::LBANameVec), + Regular(RawData), + Main(RawData, overlay::LBANameVec), + Overlay(RawData, overlay::LBANameVec), + XA_Audio(Vec), } pub struct File { @@ -229,22 +231,36 @@ pub struct File { } impl File { - pub fn new_regular(file_name: &str, content: Vec) -> Result { + pub fn new_regular(file_name: &str, content: RawData) -> Result { let content_size = content.len(); Self::new_from_content(file_name, FileType::Regular(content), content_size) } - pub fn new_main(file_name: &str, content: Vec, lba_names: overlay::LBANameVec) -> Result { + pub fn new_main(file_name: &str, content: RawData, lba_names: overlay::LBANameVec) -> Result { let content_size = content.len(); Self::new_from_content(file_name, FileType::Main(content, lba_names), content_size) } - pub fn new_overlay(file_name: &str, content: Vec, lba_names: overlay::LBANameVec, content_size: usize) -> Result { + pub fn new_overlay(file_name: &str, content: RawData, lba_names: overlay::LBANameVec, content_size: usize) -> Result { Self::new_from_content(file_name, FileType::Overlay(content, lba_names), content_size) } + pub fn new_xa_audio(file_name: &str, content: Vec) -> Result { + let channel_count = content.len(); + let highest_size = { + let mut size = 0; + for channel in &content { + if channel.len() > size { + size = channel.len(); + } + } + size + }; + Self::new_from_content(file_name, FileType::XA_Audio(content), highest_size*xa_audio_interleave_count(channel_count)) + } + pub fn get_track_rel_lba(&self) -> usize { self.properties.lba.get_track_relative() } @@ -257,7 +273,7 @@ impl File { self.properties.get_padded_size() } - pub(super) fn make_regular(&mut self, content: Vec) { + pub(super) fn make_regular(&mut self, content: RawData) { self.properties.size_bytes = content.len(); self.content = FileType::Regular(content); } diff --git a/src/Tools/psxcdgen_ex/src/types/overlay/main.rs b/src/Tools/psxcdgen_ex/src/types/overlay/main.rs index 8e162849..7417c63c 100644 --- a/src/Tools/psxcdgen_ex/src/types/overlay/main.rs +++ b/src/Tools/psxcdgen_ex/src/types/overlay/main.rs @@ -1,4 +1,6 @@ -pub fn skip_to_lba_area(content: &mut Vec) -> &mut [u8] { +use crate::types::RawData; + +pub fn skip_to_lba_area(content: &mut RawData) -> &mut [u8] { const PSX_HEADER_SIZE:usize = 2048; &mut content[PSX_HEADER_SIZE..] diff --git a/src/Tools/psxcdgen_ex/src/types/overlay/mod.rs b/src/Tools/psxcdgen_ex/src/types/overlay/mod.rs index 6603f2d9..106c8955 100644 --- a/src/Tools/psxcdgen_ex/src/types/overlay/mod.rs +++ b/src/Tools/psxcdgen_ex/src/types/overlay/mod.rs @@ -1,3 +1,4 @@ +use super::RawData; use super::{layout::Layout, File, FileSystemMap}; use super::super::encoder::LengthCalculatorFunction; use std::path::PathBuf; @@ -52,11 +53,11 @@ pub fn load_from(file_name: &str, file_path: &PathBuf, lba_source: PathBuf) -> R Ok(File::new_overlay(file_name, content, lba_names, content_size)?) } -pub fn load_for_main(file_name: &str, content: Vec, lba_source: PathBuf) -> Result { +pub fn load_for_main(file_name: &str, content: RawData, lba_source: PathBuf) -> Result { File::new_main(file_name, content, load_lba_names(lba_source)?) } -pub fn update_content(content: &mut Vec, lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction) -> Result, Error> { +pub fn update_content(content: &mut RawData, lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction) -> Result { let lba_header = skip_to_lba_header(content); let lba_header = unsafe{std::slice::from_raw_parts_mut(lba_header.as_mut_ptr() as *mut LBAEntry, lba_names.len())}; @@ -67,7 +68,7 @@ pub fn update_content(content: &mut Vec, lba_names: &LBANameVec, file_map: & Ok(tool_helper::compress::psx_default::lz4(content)?) } -pub fn update_content_for_main(content: &mut Vec, lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction) -> Result, Error> { +pub fn update_content_for_main(content: &mut RawData, lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction) -> Result { let lba_header = unsafe{std::slice::from_raw_parts_mut(main::skip_to_lba_area(content).as_mut_ptr() as *mut LBAEntry, lba_names.len())}; for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes), is_lz4| { @@ -97,7 +98,7 @@ fn for_each_lba_name Result<(), Error>> Ok(()) } -fn skip_to_lba_header(content: &mut Vec) -> &mut [u8] { +fn skip_to_lba_header(content: &mut RawData) -> &mut [u8] { let overlay_header_size = 0; &mut content[overlay_header_size..]