Prepare to support enconding of LZ4

This commit is contained in:
Jaby 2023-04-13 21:06:24 +02:00
parent a2c7af2e12
commit 1dd145abea
5 changed files with 65 additions and 23 deletions

View File

@ -2,6 +2,12 @@ use super::Error;
use std::path::PathBuf;
mod xml;
pub enum LZ4State {
None,
AlreadyCompressed,
Compress,
}
pub struct Configuration {
pub publisher: Option<String>,
pub license_path: Option<PathBuf>,
@ -17,6 +23,7 @@ impl Configuration {
pub struct CommonProperties {
pub name: String,
pub is_hidden: bool,
pub lz4_state: LZ4State,
pub padded_size: Option<usize>,
}

View File

@ -2,13 +2,14 @@ use std::path::PathBuf;
use tool_helper::{format_if_error, path_with_env_from};
use crate::config_reader::Directory;
use super::{CommonProperties, Configuration, Error, File, FileKind};
use super::{CommonProperties, Configuration, Error, File, FileKind, LZ4State};
mod attribute_names {
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 fn parse(xml: String) -> Result<Configuration, Error> {
@ -57,27 +58,28 @@ fn parse_description(description: roxmltree::Node, config: &mut Configuration) -
fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), Error> {
fn parse_regular_file(file: roxmltree::Node, is_hidden: bool) -> Result<File, Error> {
let common = read_common_properties(&file, is_hidden)?;
let common = read_common_properties(&file, is_hidden, None)?;
let path = path_from_node(&file, &common.name)?;
Ok(File{common, path, kind: FileKind::Regular})
}
fn parse_main_file(file: roxmltree::Node, is_hidden: bool) -> Result<File, Error> {
fn parse_main_file(file: roxmltree::Node) -> Result<File, Error> {
if let Some(lba_path) = file.attribute(attribute_names::LBA_SOURCE) {
let common = read_common_properties(&file, is_hidden)?;
let common = read_common_properties(&file, false, Some(LZ4State::None))?;
let path = path_from_node(&file, &common.name)?;
Ok(File{common, path, kind: FileKind::Main(PathBuf::from(lba_path))})
}
else {
parse_regular_file(file, is_hidden)
parse_regular_file(file, false)
}
}
fn parse_overlay_file(file: roxmltree::Node, is_hidden: bool) -> Result<File, Error> {
let common = read_common_properties(&file, is_hidden)?;
// 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()))})
@ -88,7 +90,7 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(),
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, is_hidden)?),
"Main" => root.add_file(parse_main_file(node)?),
"Overlay" => root.add_file(parse_overlay_file(node, is_hidden)?),
"Directory" => {
is_hidden |= parse_boolean_attribute(&node, attribute_names::HIDDEN)?;
@ -108,10 +110,31 @@ fn parse_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(),
parse_file_system(track, &mut config.root, false)
}
fn read_common_properties(xml: &roxmltree::Node, is_hidden: bool) -> Result<CommonProperties, Error> {
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 {
forced_lz4_state
}
else {
if let Some(state_str) = xml.attribute(attribute_names::LZ4_STATE) {
match state_str.to_lowercase().as_str() {
"yes" => LZ4State::Compress,
"already" => LZ4State::AlreadyCompressed,
_ => LZ4State::None,
}
}
else {
LZ4State::None
}
}
};
Ok(CommonProperties{
name: String::from(xml.attribute(attribute_names::NAME).unwrap_or_default()),
is_hidden: is_hidden | parse_boolean_attribute(&xml, attribute_names::HIDDEN)?,
lz4_state,
padded_size: read_padded_size(&xml)?
})
}

View File

@ -5,6 +5,7 @@ pub mod encoder;
pub mod file_writer;
pub mod types;
use config_reader::LZ4State;
use encoder::{LbaCalculatorFunction, LengthCalculatorFunction};
use tool_helper::{format_if_error, Output, read_file};
use types::{layout::Layout, CDDesc, Directory, File, FileType, FileSystemMap, Properties, SharedPtr};
@ -160,13 +161,21 @@ fn parse_configuration(config: config_reader::Configuration) -> Result<(CDDesc,
match file.kind {
config_reader::FileKind::Regular => (types::File::new_regular(file.common.name.as_str(), read_file(&file.path)?)?, false),
config_reader::FileKind::Main(lba_source) => (types::overlay::load_for_main(file.common.name.as_str(), read_file(&file.path)?, 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::Overlay(lba_source) => (types::overlay::load_from(file.common.name.as_str(), &file.path, lba_source)?, true),
}
};
desc_file.properties.padded_size_bytes = file.common.padded_size;
desc_file.properties.is_hidden = file.common.is_hidden;
match file.common.lz4_state {
LZ4State::None => desc_file.properties.is_lz4 = false,
LZ4State::AlreadyCompressed => desc_file.properties.is_lz4 = true,
LZ4State::Compress => {
return Err(Error::from_text(format!("LZ4 compression requested for file {} but feature is not supported yet", &file.path.to_string_lossy())));
}
}
let new_file = dst_dir.add_file(desc_file);
if needs_treatment {
lba_embedded_files.push(new_file);

View File

@ -316,7 +316,8 @@ pub struct Properties {
pub(super) lba: LBA,
pub(super) size_bytes: usize,
pub(super) padded_size_bytes: Option<usize>,
pub(super) is_hidden: bool
pub(super) is_hidden: bool,
pub(super) is_lz4: bool,
}
impl Properties {
@ -341,7 +342,7 @@ impl Properties {
impl Default for Properties {
fn default() -> Self {
Properties{lba: LBA::default(), size_bytes: 0, padded_size_bytes: None, is_hidden: false}
Properties{lba: LBA::default(), size_bytes: 0, padded_size_bytes: None, is_hidden: false, is_lz4: false}
}
}

View File

@ -23,7 +23,7 @@ impl LBAEntry {
const _IS_LZ4_COMPRESSED:Bit = Bit::at(19);
const LBA_VALUE_RANGE:BitRange = BitRange::from_to(0, 18);
pub fn write_entry(&mut self, lba: usize, size_bytes: usize) -> Result<(), Error> {
pub fn write_entry(&mut self, lba: usize, size_bytes: usize, _is_lz4: bool) -> Result<(), Error> {
if lba > Self::LBA_VALUE_RANGE.max_value() {
return Err(Error::from_text(format!("LBA of value {} is impossible and can not be encoded! Maximum LBA value is: {}", lba, Self::LBA_VALUE_RANGE.max_value())));
}
@ -45,8 +45,8 @@ impl LBAEntry {
}
}
pub fn load_from(file_name: &str, file_path: PathBuf, lba_source: PathBuf) -> Result<File, Error> {
let content = read_file(&file_path)?;
pub fn load_from(file_name: &str, file_path: &PathBuf, lba_source: PathBuf) -> Result<File, Error> {
let content = read_file(file_path)?;
let lba_names = load_lba_names(lba_source)?;
let content_size = format_if_error!(tool_helper::compress::psx_default::lz4(&content), "Compressing {} failed with \"{error_text}\"", file_path.to_string_lossy())?.len();
@ -61,8 +61,8 @@ pub fn update_content(content: &mut Vec<u8>, lba_names: &LBANameVec, file_map: &
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())};
for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes)| {
lba_header[idx].write_entry(lba, bytes)
for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes), is_lz4| {
lba_header[idx].write_entry(lba, bytes, is_lz4)
})?;
Ok(tool_helper::compress::psx_default::lz4(content)?)
@ -71,20 +71,22 @@ pub fn update_content(content: &mut Vec<u8>, lba_names: &LBANameVec, file_map: &
pub fn update_content_for_main(content: &mut Vec<u8>, lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction) -> Result<Vec<u8>, Error> {
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)| {
lba_header[idx].write_entry(lba, bytes)
for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes), is_lz4| {
lba_header[idx].write_entry(lba, bytes, is_lz4)
})?;
Ok(content.clone())
}
fn for_each_lba_name<F: FnMut(usize, (usize, usize)) -> Result<(), Error>>(lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction, mut functor: F) -> Result<(), Error> {
fn for_each_lba_name<F: FnMut(usize, (usize, usize), bool) -> Result<(), Error>>(lba_names: &LBANameVec, file_map: &FileSystemMap, length_func: LengthCalculatorFunction, mut functor: F) -> Result<(), Error> {
let mut idx = 0;
for lba_name in lba_names {
if let Some(file) = file_map.get(lba_name) {
let (lba, bytes) = (format_if_error_drop_cause!(file.try_borrow(), "Failed accessing file \"{}\" for writing LBA information.\nNote: You can not inject the LBA information of a file into itself.", lba_name)?.get_absolute_lba(), length_func(&Layout::File(file.clone())).bytes);
let file_ref = format_if_error_drop_cause!(file.try_borrow(), "Failed accessing file \"{}\" for writing LBA information.\nNote: You can not inject the LBA information of a file into itself.", lba_name)?;
let (lba, bytes) = (file_ref.get_absolute_lba(), length_func(&Layout::File(file.clone())).bytes);
let is_lz4 = file_ref.properties.is_lz4;
functor(idx, (lba, bytes.ok_or(Error::from_text(format!("{} does not contain a size!", lba_name)))?))?;
functor(idx, (lba, bytes.ok_or(Error::from_text(format!("{} does not contain a size!", lba_name)))?), is_lz4)?;
idx += 1;
}