Prepare to support enconding of LZ4
This commit is contained in:
parent
a2c7af2e12
commit
1dd145abea
|
@ -2,6 +2,12 @@ use super::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
mod xml;
|
mod xml;
|
||||||
|
|
||||||
|
pub enum LZ4State {
|
||||||
|
None,
|
||||||
|
AlreadyCompressed,
|
||||||
|
Compress,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub publisher: Option<String>,
|
pub publisher: Option<String>,
|
||||||
pub license_path: Option<PathBuf>,
|
pub license_path: Option<PathBuf>,
|
||||||
|
@ -17,6 +23,7 @@ impl Configuration {
|
||||||
pub struct CommonProperties {
|
pub struct CommonProperties {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_hidden: bool,
|
pub is_hidden: bool,
|
||||||
|
pub lz4_state: LZ4State,
|
||||||
pub padded_size: Option<usize>,
|
pub padded_size: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,14 @@ use std::path::PathBuf;
|
||||||
use tool_helper::{format_if_error, path_with_env_from};
|
use tool_helper::{format_if_error, path_with_env_from};
|
||||||
use crate::config_reader::Directory;
|
use crate::config_reader::Directory;
|
||||||
|
|
||||||
use super::{CommonProperties, Configuration, Error, File, FileKind};
|
use super::{CommonProperties, Configuration, Error, File, FileKind, LZ4State};
|
||||||
|
|
||||||
mod attribute_names {
|
mod attribute_names {
|
||||||
pub const NAME: &'static str = "name";
|
pub const NAME: &'static str = "name";
|
||||||
pub const HIDDEN: &'static str = "hidden";
|
pub const HIDDEN: &'static str = "hidden";
|
||||||
pub const PADDED_SIZE: &'static str = "padded_size";
|
pub const PADDED_SIZE: &'static str = "padded_size";
|
||||||
pub const LBA_SOURCE: &'static str = "lba_source";
|
pub const LBA_SOURCE: &'static str = "lba_source";
|
||||||
|
pub const LZ4_STATE: &'static str = "lz4";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(xml: String) -> Result<Configuration, Error> {
|
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_track(track: roxmltree::Node, config: &mut Configuration) -> Result<(), Error> {
|
||||||
fn parse_regular_file(file: roxmltree::Node, is_hidden: bool) -> Result<File, 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)?;
|
let path = path_from_node(&file, &common.name)?;
|
||||||
|
|
||||||
Ok(File{common, path, kind: FileKind::Regular})
|
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) {
|
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)?;
|
let path = path_from_node(&file, &common.name)?;
|
||||||
|
|
||||||
Ok(File{common, path, kind: FileKind::Main(PathBuf::from(lba_path))})
|
Ok(File{common, path, kind: FileKind::Main(PathBuf::from(lba_path))})
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
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> {
|
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)?;
|
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()))})
|
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() {
|
if node.is_element() {
|
||||||
match node.tag_name().name() {
|
match node.tag_name().name() {
|
||||||
"File" => root.add_file(parse_regular_file(node, is_hidden)?),
|
"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)?),
|
"Overlay" => root.add_file(parse_overlay_file(node, is_hidden)?),
|
||||||
"Directory" => {
|
"Directory" => {
|
||||||
is_hidden |= parse_boolean_attribute(&node, attribute_names::HIDDEN)?;
|
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)
|
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{
|
Ok(CommonProperties{
|
||||||
name: String::from(xml.attribute(attribute_names::NAME).unwrap_or_default()),
|
name: String::from(xml.attribute(attribute_names::NAME).unwrap_or_default()),
|
||||||
is_hidden: is_hidden | parse_boolean_attribute(&xml, attribute_names::HIDDEN)?,
|
is_hidden: is_hidden | parse_boolean_attribute(&xml, attribute_names::HIDDEN)?,
|
||||||
|
lz4_state,
|
||||||
padded_size: read_padded_size(&xml)?
|
padded_size: read_padded_size(&xml)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub mod encoder;
|
||||||
pub mod file_writer;
|
pub mod file_writer;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
use config_reader::LZ4State;
|
||||||
use encoder::{LbaCalculatorFunction, LengthCalculatorFunction};
|
use encoder::{LbaCalculatorFunction, LengthCalculatorFunction};
|
||||||
use tool_helper::{format_if_error, Output, read_file};
|
use tool_helper::{format_if_error, Output, read_file};
|
||||||
use types::{layout::Layout, CDDesc, Directory, File, FileType, FileSystemMap, Properties, SharedPtr};
|
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 {
|
match file.kind {
|
||||||
config_reader::FileKind::Regular => (types::File::new_regular(file.common.name.as_str(), read_file(&file.path)?)?, false),
|
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::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.padded_size_bytes = file.common.padded_size;
|
||||||
desc_file.properties.is_hidden = file.common.is_hidden;
|
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);
|
let new_file = dst_dir.add_file(desc_file);
|
||||||
if needs_treatment {
|
if needs_treatment {
|
||||||
lba_embedded_files.push(new_file);
|
lba_embedded_files.push(new_file);
|
||||||
|
|
|
@ -316,7 +316,8 @@ pub struct Properties {
|
||||||
pub(super) lba: LBA,
|
pub(super) lba: LBA,
|
||||||
pub(super) size_bytes: usize,
|
pub(super) size_bytes: usize,
|
||||||
pub(super) padded_size_bytes: Option<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 {
|
impl Properties {
|
||||||
|
@ -341,7 +342,7 @@ impl Properties {
|
||||||
|
|
||||||
impl Default for Properties {
|
impl Default for Properties {
|
||||||
fn default() -> Self {
|
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}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl LBAEntry {
|
||||||
const _IS_LZ4_COMPRESSED:Bit = Bit::at(19);
|
const _IS_LZ4_COMPRESSED:Bit = Bit::at(19);
|
||||||
const LBA_VALUE_RANGE:BitRange = BitRange::from_to(0, 18);
|
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() {
|
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())));
|
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> {
|
pub fn load_from(file_name: &str, file_path: &PathBuf, lba_source: PathBuf) -> Result<File, Error> {
|
||||||
let content = read_file(&file_path)?;
|
let content = read_file(file_path)?;
|
||||||
let lba_names = load_lba_names(lba_source)?;
|
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();
|
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 = 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())};
|
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)| {
|
for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes), is_lz4| {
|
||||||
lba_header[idx].write_entry(lba, bytes)
|
lba_header[idx].write_entry(lba, bytes, is_lz4)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(tool_helper::compress::psx_default::lz4(content)?)
|
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> {
|
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())};
|
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)| {
|
for_each_lba_name(lba_names, file_map, length_func, |idx, (lba, bytes), is_lz4| {
|
||||||
lba_header[idx].write_entry(lba, bytes)
|
lba_header[idx].write_entry(lba, bytes, is_lz4)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(content.clone())
|
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;
|
let mut idx = 0;
|
||||||
for lba_name in lba_names {
|
for lba_name in lba_names {
|
||||||
if let Some(file) = file_map.get(lba_name) {
|
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;
|
idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue