From 8e645f3a71dab16c5e6e6426fd8d2dc92bceb446 Mon Sep 17 00:00:00 2001 From: jaby Date: Wed, 21 Aug 2024 21:44:26 +0200 Subject: [PATCH] Support alternate license when none is provided or found --- examples/PoolBox/iso/Config.xml | 6 +- src/Tools/psxcdgen_ex/Cargo.toml | 2 +- .../psxcdgen_ex/src/config_reader/mod.rs | 13 +- .../psxcdgen_ex/src/config_reader/xml.rs | 126 ++++++++++++------ src/Tools/psxcdgen_ex/src/encoder/cd.rs | 32 +++-- src/Tools/psxcdgen_ex/src/lib.rs | 3 +- src/Tools/psxcdgen_ex/src/types/mod.rs | 11 +- template/JabyEngine-PSX_Game/iso/Config.xml | 17 ++- 8 files changed, 138 insertions(+), 72 deletions(-) diff --git a/examples/PoolBox/iso/Config.xml b/examples/PoolBox/iso/Config.xml index 7f4f2699..f7dacfa4 100644 --- a/examples/PoolBox/iso/Config.xml +++ b/examples/PoolBox/iso/Config.xml @@ -9,8 +9,10 @@ --> Jaby - - %JABY_ENGINE_DIR%/bin/extern/32BIT.TMD + + %PSX_LICENSE_PATH%/%PSX_LICENSE%.DAT + + System.cnf.subst diff --git a/src/Tools/psxcdgen_ex/Cargo.toml b/src/Tools/psxcdgen_ex/Cargo.toml index 60795634..c15d93df 100644 --- a/src/Tools/psxcdgen_ex/Cargo.toml +++ b/src/Tools/psxcdgen_ex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "psxcdgen_ex" -version = "1.0.1" +version = "1.1.0" edition = "2021" [profile.release] diff --git a/src/Tools/psxcdgen_ex/src/config_reader/mod.rs b/src/Tools/psxcdgen_ex/src/config_reader/mod.rs index f1283535..9ae17022 100644 --- a/src/Tools/psxcdgen_ex/src/config_reader/mod.rs +++ b/src/Tools/psxcdgen_ex/src/config_reader/mod.rs @@ -21,16 +21,17 @@ impl CDAudioFile { } pub struct Configuration { - pub publisher: Option, - pub license_file: LicenseFile, - pub root: Directory, - pub cd_audio_files: Vec, - pub lead_out_sectors: Option, + pub publisher: Option, + pub license_file: LicenseFile, + pub backup_license_file: LicenseFile, + pub root: Directory, + pub cd_audio_files: Vec, + pub lead_out_sectors: Option, } impl Configuration { pub fn new() -> Configuration { - Configuration{publisher: None, license_file: LicenseFile::None, root: Directory::new("root", false), cd_audio_files: Vec::new(), lead_out_sectors: None} + Configuration{publisher: None, license_file: LicenseFile::None, backup_license_file: LicenseFile::None, root: Directory::new("root", false), cd_audio_files: Vec::new(), lead_out_sectors: None} } } diff --git a/src/Tools/psxcdgen_ex/src/config_reader/xml.rs b/src/Tools/psxcdgen_ex/src/config_reader/xml.rs index e7345602..163383d1 100644 --- a/src/Tools/psxcdgen_ex/src/config_reader/xml.rs +++ b/src/Tools/psxcdgen_ex/src/config_reader/xml.rs @@ -1,27 +1,53 @@ -use attribute_names::LBA_SOURCE; use cdtypes::types::time::Time; use std::path::PathBuf; -use tool_helper::{format_if_error, path_with_env_from, string_with_env_from}; +use tool_helper::{format_if_error, path_with_env_from, print_warning, string_with_env_from}; use crate::{config_reader::Directory, types::LicenseFile}; use super::{CDAudioFile, CommonProperties, Configuration, Error, File, FileKind, LZ4State}; -mod attribute_names { +mod license_tag { + pub const NAME: &'static str = "License"; + pub mod attribute { + pub const ALT_LOGO: &'static str = "alt-logo"; + pub const ALT_TEXT: &'static str = "alt-text"; + } +} + +mod root_tag { + pub const NAME: &'static str = "PSXCD"; + pub mod attribute {} +} + +mod filesystem_tag { + pub const NAME: &'static str = "Filesystem"; + pub mod attribute { + pub const LEAD_OUT:&'static str = "lead-out"; + } +} + +mod file_and_directory_attribute { pub const NAME: &'static str = "name"; pub const HIDDEN: &'static str = "hidden"; pub const PADDED_SIZE: &'static str = "padded_size"; pub const LBA_SOURCE: &'static str = "lba_source"; - pub const LZ4_STATE: &'static str = "lz4"; - pub const ALTLICENSE_TEXT: &'static str = "text"; + pub const LZ4_STATE: &'static str = "lz4"; } -mod tag_names { - pub const ROOT: &'static str = "PSXCD"; - pub const TRACK: &'static str = "Filesystem"; - pub const INTERLEAVED: &'static str = "InterleavedFile"; - pub const CDDA: &'static str = "AudioTrack"; - pub const LICENSE: &'static str = "License"; - pub const ALTLICENSE: &'static str = "AltLicense"; +mod interleaved_file_tag { + pub const NAME: &'static str = "InterleavedFile"; + pub mod attribute {} +} + +mod cdda_tag { + pub const NAME: &'static str = "AltLicense"; + pub mod attribute {} +} + +mod alt_license_tag { + pub const NAME: &'static str = "AudioTrack"; + pub mod attribute { + pub const TEXT: &'static str = "text"; + } } pub fn parse(xml: String) -> Result { @@ -30,7 +56,7 @@ pub fn parse(xml: String) -> Result { let children = parser.root().children(); for node in children { - if node.is_element() && node.tag_name().name() == tag_names::ROOT { + if node.is_element() && node.tag_name().name() == root_tag::NAME { parse_root(node, &mut config)?; } } @@ -44,8 +70,8 @@ fn parse_root(root: roxmltree::Node, config: &mut Configuration) -> Result<(), E for node in root.children() { if node.is_element() { match node.tag_name().name() { - "Description" => parse_description(node, config), - tag_names::TRACK => { + "Description" => parse_description(node, config), + filesystem_tag::NAME => { if !parsed_track { parsed_track = true; parse_track(node, config) @@ -55,7 +81,7 @@ fn parse_root(root: roxmltree::Node, config: &mut Configuration) -> Result<(), E Err(Error::from_text(format!("Found more than 1 filesystem. Multi filesystem discs are not supported"))) } }, - tag_names::CDDA => parse_cd_audio(node, config), + cdda_tag::NAME => parse_cd_audio(node, config), _ => Ok(()) }?; } @@ -64,20 +90,39 @@ fn parse_root(root: roxmltree::Node, config: &mut Configuration) -> Result<(), E } fn parse_description(description: roxmltree::Node, config: &mut Configuration) -> Result<(), Error> { - fn parse_license_path(license: roxmltree::Node) -> LicenseFile { - LicenseFile::from_authentic_option(path_from_node(&license, tag_names::LICENSE).ok()) + fn parse_license_path(license: roxmltree::Node) -> (LicenseFile, LicenseFile) { + let required_lic_file = LicenseFile::from_authentic_option(path_from_node(&license, license_tag::NAME).ok()); + let alt_logo = { + if let Some(path) = license.attribute(license_tag::attribute::ALT_LOGO) { + Some(path_with_env_from(path)) + } + + else { + None + } + }; + let alt_text = license.attribute(license_tag::attribute::ALT_TEXT); + (required_lic_file, LicenseFile::from_tmd_option(alt_logo, alt_text)) } fn parse_altlicense_path(license: roxmltree::Node) -> LicenseFile { - LicenseFile::from_tmd_option(path_from_node(&license, tag_names::ALTLICENSE).ok(), license.attribute(attribute_names::ALTLICENSE_TEXT)) + LicenseFile::from_tmd_option(path_from_node(&license, alt_license_tag::NAME).ok(), license.attribute(alt_license_tag::attribute::TEXT)) } for node in description.descendants() { if node.is_element() { match node.tag_name().name() { - "Publisher" => config.publisher = Some(String::from(node.text().unwrap_or_default())), - tag_names::LICENSE => config.license_file = parse_license_path(node), - tag_names::ALTLICENSE => config.license_file = parse_altlicense_path(node), + "Publisher" => config.publisher = Some(String::from(node.text().unwrap_or_default())), + license_tag::NAME => (config.license_file, config.backup_license_file) = parse_license_path(node), + alt_license_tag::NAME => { + if matches!(config.license_file, LicenseFile::None) { + config.license_file = parse_altlicense_path(node) + } + + else { + print_warning("Alternate license ignored because a (alternate) license was already provided".to_owned()) + } + }, _ => () } } @@ -94,7 +139,7 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), } fn parse_main_file(file: roxmltree::Node) -> Result { - if let Some(lba_path) = file.attribute(attribute_names::LBA_SOURCE) { + if let Some(lba_path) = file.attribute(file_and_directory_attribute::LBA_SOURCE) { let common = read_common_properties(&file, false, Some(LZ4State::None))?; let path = path_from_node(&file, &common.name)?; @@ -102,7 +147,7 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), } else { - tool_helper::print_warning(format!("The main file should always contain the \"{}\" attribute, even when just empty", LBA_SOURCE)); + tool_helper::print_warning(format!("The main file should always contain the \"{}\" attribute, even when just empty", file_and_directory_attribute::LBA_SOURCE)); parse_regular_file(file, false) } } @@ -112,12 +157,12 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), let common = read_common_properties(&file, is_hidden, Some(LZ4State::AlreadyCompressed))?; let path = path_from_node(&file, &common.name)?; let lba_source = { - if let Some(lba_source) = file.attribute(attribute_names::LBA_SOURCE) { + if let Some(lba_source) = file.attribute(file_and_directory_attribute::LBA_SOURCE) { lba_source } else { - tool_helper::print_warning(format!("Overlays should always contain the \"{}\" attribute, even when just empty", LBA_SOURCE)); + tool_helper::print_warning(format!("Overlays should always contain the \"{}\" attribute, even when just empty", file_and_directory_attribute::LBA_SOURCE)); "" } }; @@ -145,13 +190,13 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), for node in cur_node.children() { if node.is_element() { match node.tag_name().name() { - "File" => root.add_file(parse_regular_file(node, is_hidden)?), - "Main" => root.add_file(parse_main_file(node)?), - "Overlay" => root.add_file(parse_overlay_file(node, is_hidden)?), - tag_names::INTERLEAVED => root.add_file(parse_interleaved(node, is_hidden)?), - "Directory" => { - is_hidden |= parse_boolean_attribute(&node, attribute_names::HIDDEN)?; - let mut new_dir = Directory::new(node.attribute(attribute_names::NAME).unwrap_or_default(), is_hidden); + "File" => root.add_file(parse_regular_file(node, is_hidden)?), + "Main" => root.add_file(parse_main_file(node)?), + "Overlay" => root.add_file(parse_overlay_file(node, is_hidden)?), + interleaved_file_tag::NAME => root.add_file(parse_interleaved(node, is_hidden)?), + "Directory" => { + is_hidden |= parse_boolean_attribute(&node, file_and_directory_attribute::HIDDEN)?; + let mut new_dir = Directory::new(node.attribute(file_and_directory_attribute::NAME).unwrap_or_default(), is_hidden); parse_file_system(node, &mut new_dir, is_hidden)?; root.add_dir(new_dir); @@ -165,7 +210,6 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), } fn get_lead_out_sectors(track: roxmltree::Node) -> Result, Error> { - const ATTRIBUTE_NAME:&'static str = "lead-out"; const MIN_OPTION_NAME:&'static str = "minutes"; const SECOND_OPTION_NAME:&'static str = "seconds"; const SECTOR_OPTION_NAME:&'static str = "sectors"; @@ -173,13 +217,13 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), fn parse_split<'a>(who: &'a str, str_opt: Option<&'a str>) -> Result { match str_opt.unwrap_or("0").parse::() { Ok(num) => Ok(num), - Err(error) => Err(Error::from_text(format!("Failed converting \"{}\" option for \"{}\" attribute \"{}\": {}", who, tag_names::TRACK, ATTRIBUTE_NAME, error))) + Err(error) => Err(Error::from_text(format!("Failed converting \"{}\" option for \"{}\" attribute \"{}\": {}", who, filesystem_tag::NAME, filesystem_tag::attribute::LEAD_OUT, error))) } } //--------------------------------------------------------------------- - if let Some(length_str) = track.attribute(ATTRIBUTE_NAME) { + if let Some(length_str) = track.attribute(filesystem_tag::attribute::LEAD_OUT) { let mut pieces = length_str.split(':'); let (mins, seconds, sectors) = ( parse_split(MIN_OPTION_NAME, pieces.next())?, @@ -211,7 +255,7 @@ fn read_common_properties(xml: &roxmltree::Node, is_hidden: bool, force_lz4_stat } else { - if let Some(state_str) = xml.attribute(attribute_names::LZ4_STATE) { + if let Some(state_str) = xml.attribute(file_and_directory_attribute::LZ4_STATE) { match state_str.to_lowercase().as_str() { "yes" => LZ4State::Compress, "already" => LZ4State::AlreadyCompressed, @@ -226,15 +270,15 @@ fn read_common_properties(xml: &roxmltree::Node, is_hidden: bool, force_lz4_stat }; Ok(CommonProperties{ - name: string_with_env_from(xml.attribute(attribute_names::NAME).unwrap_or_default()), - is_hidden: is_hidden | parse_boolean_attribute(&xml, attribute_names::HIDDEN)?, + name: string_with_env_from(xml.attribute(file_and_directory_attribute::NAME).unwrap_or_default()), + is_hidden: is_hidden | parse_boolean_attribute(&xml, file_and_directory_attribute::HIDDEN)?, lz4_state, padded_size: read_padded_size(&xml)? }) } fn read_padded_size(xml: &roxmltree::Node) -> Result, Error> { - if let Some(padded_attr) = xml.attribute(attribute_names::PADDED_SIZE) { + if let Some(padded_attr) = xml.attribute(file_and_directory_attribute::PADDED_SIZE) { let padded_size = format_if_error!(padded_attr.parse::(), "Failed reading \"{}\" as padded size: {error_text}", padded_attr)?; Ok(Some(padded_size)) } @@ -262,7 +306,7 @@ fn parse_boolean_attribute(xml: &roxmltree::Node, attribute_name: &str) -> Resul fn path_from_node(node: &roxmltree::Node, name: &str) -> Result { if let Some(path) = node.text() { - Ok(path_with_env_from(path)) + Ok(path_with_env_from(path.trim())) } else { diff --git a/src/Tools/psxcdgen_ex/src/encoder/cd.rs b/src/Tools/psxcdgen_ex/src/encoder/cd.rs index 7c31447f..f4d6e8b5 100644 --- a/src/Tools/psxcdgen_ex/src/encoder/cd.rs +++ b/src/Tools/psxcdgen_ex/src/encoder/cd.rs @@ -3,7 +3,7 @@ use super::super::types::{FileType, helper::{DirectoryRecordMember, PathTableMem use builder::SubModeBuilder; use cdtypes::types::{cdstring::{AString, DString}, date::*, dir_record::*, helper::{round_bytes_mode2_form1, sector_count_mode2_form1, sector_count_mode2_form2}, lsb_msb::*, path_table::*, pvd as cd_pvd, sector::Mode2Form1}; use tool_helper::{BufferedInputFile, format_if_error, open_input_file_buffered, print_warning}; -use std::io::{Read, Seek, SeekFrom}; +use std::{io::{Read, Seek, SeekFrom}, path::PathBuf}; const ROOT_DIR_NAME:&'static str = "\x00"; @@ -296,18 +296,32 @@ fn process_system_area(system_area: &SystemArea, sec_writer: &mut dyn SectorWrit return Err(Error::from_text(format!("System Area required to start at sector 0 of Track - found LBA: {}", system_area_lba))); } + fn write_tmd_license(sec_writer: &mut dyn SectorWriter, tmd_path: &PathBuf, lic_text: &Option) -> Result<(), Error> { + print_warning(format!("WARNING: An alternative license file was provided. {}", NOT_BOOTING_CD_STR)); + let tmd_file = format_if_error!(open_input_file_buffered(tmd_path), "Loading TMD file from {} failed with: {error_text}", tmd_path.to_string_lossy())?; + write_tmd_file(sec_writer, tmd_file, lic_text) + } + const NOT_BOOTING_CD_STR:&'static str = "Some emulators (No$PSX) and some consoles (japanese/some european PS1; All PS3) will not boot this CD."; match &system_area.license_file { LicenseFile::Authentic(license_path) => { - let license_file = format_if_error!(open_input_file_buffered(license_path), "Loading license file from {} failed with: {error_text}", license_path.to_string_lossy())?; - write_license_file(sec_writer, license_file) + match open_input_file_buffered(license_path) { + Ok(license_file) => write_license_file(sec_writer, license_file), + Err(error) => { + let err_str = format!("Loading license file from {} failed with: {}", license_path.to_string_lossy(), error); + if let LicenseFile::TMD(tmd_path, lic_text) = &system_area.backup_license_file { + print_warning(format!("{}. Now using backup license information", err_str)); + write_tmd_license(sec_writer, tmd_path, lic_text) + } + + else { + Err(Error::from_text(err_str)) + } + } + } }, - LicenseFile::TMD(tmd_path, lic_text) => { - print_warning(format!("WARNING: An alternative license file was provided. {}", NOT_BOOTING_CD_STR)); - let tmd_file = format_if_error!(open_input_file_buffered(tmd_path), "Loading TMD file from {} failed with: {error_text}", tmd_path.to_string_lossy())?; - write_tmd_file(sec_writer, tmd_file, lic_text) - }, - LicenseFile::None => { + LicenseFile::TMD(tmd_path, lic_text) => write_tmd_license(sec_writer, tmd_path, lic_text), + LicenseFile::None => { // No license specified - filling it with zeros print_warning(format!("WARNING: No license file provided. {}", NOT_BOOTING_CD_STR)); write_dummy(sec_writer, size_of::SYSTEM_AREA_SECTOR_COUNT) diff --git a/src/Tools/psxcdgen_ex/src/lib.rs b/src/Tools/psxcdgen_ex/src/lib.rs index 408ee89f..3c5b246a 100644 --- a/src/Tools/psxcdgen_ex/src/lib.rs +++ b/src/Tools/psxcdgen_ex/src/lib.rs @@ -261,7 +261,8 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc, cd_desc.pvd.borrow_mut().set_publisher(publisher); } - cd_desc.system_area.borrow_mut().license_file = config.license_file; + cd_desc.system_area.borrow_mut().license_file = config.license_file; + cd_desc.system_area.borrow_mut().backup_license_file = config.backup_license_file; 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)?; diff --git a/src/Tools/psxcdgen_ex/src/types/mod.rs b/src/Tools/psxcdgen_ex/src/types/mod.rs index acf64fcc..3e32bfa2 100644 --- a/src/Tools/psxcdgen_ex/src/types/mod.rs +++ b/src/Tools/psxcdgen_ex/src/types/mod.rs @@ -108,7 +108,7 @@ impl CDDesc { pub enum LicenseFile { None, Authentic(PathBuf), - TMD(PathBuf, Option) + TMD(PathBuf, Option), } impl LicenseFile { @@ -136,18 +136,19 @@ impl LicenseFile { else { LicenseFile::None - } + } } } pub struct SystemArea { - pub(in super) lba: LBA, - pub(in super) license_file: LicenseFile, + pub(in super) lba: LBA, + pub(in super) license_file: LicenseFile, + pub(in super) backup_license_file: LicenseFile, } impl SystemArea { pub fn new() -> SystemArea { - SystemArea{lba: LBA::default(), license_file: LicenseFile::None} + SystemArea{lba: LBA::default(), license_file: LicenseFile::None, backup_license_file: LicenseFile::None} } } diff --git a/template/JabyEngine-PSX_Game/iso/Config.xml b/template/JabyEngine-PSX_Game/iso/Config.xml index f085a63a..3eeb4649 100644 --- a/template/JabyEngine-PSX_Game/iso/Config.xml +++ b/template/JabyEngine-PSX_Game/iso/Config.xml @@ -1,10 +1,13 @@ - + - - + You + + %PSX_LICENSE_PATH%/%PSX_LICENSE%.DAT + + - + System.cnf.subst -
../application/bin/%PSX_TV_FORMAT%/PSX-release/#{ProjectName}.psexe
- -
\ No newline at end of file +
../application/bin/%PSX_TV_FORMAT%/PSX-release/#{ProjectName}.psexe
+
+ \ No newline at end of file