Make fconv delete faulty files
This commit is contained in:
parent
7ceb13863e
commit
e37321de89
|
@ -1,52 +1,52 @@
|
||||||
use clap::{Parser};
|
use clap::{Parser};
|
||||||
use cpp_out::{Configuration, Error, FileType};
|
use cpp_out::{Configuration, Error, FileType};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tool_helper::exit_with_error;
|
use tool_helper::exit_with_error;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(about = "Output a file content or stdin to a c++ header/source file", long_about = None)]
|
#[clap(about = "Output a file content or stdin to a c++ header/source file", long_about = None)]
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
#[clap(value_parser)]
|
#[clap(value_parser)]
|
||||||
input_file: Option<PathBuf>,
|
input_file: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(short='n', long="name")]
|
#[clap(short='n', long="name")]
|
||||||
data_name: String,
|
data_name: String,
|
||||||
|
|
||||||
#[clap(short='o')]
|
#[clap(short='o')]
|
||||||
output_file: PathBuf,
|
output_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configurate(cmd: &mut CommandLine) -> Result<Configuration, Error> {
|
fn configurate(cmd: &mut CommandLine) -> Result<Configuration, Error> {
|
||||||
let file_name = tool_helper::get_file_name_from_path_buf(&cmd.output_file, "output")?;
|
let file_name = tool_helper::get_file_name_from_path_buf(&cmd.output_file, "output")?;
|
||||||
let extension = tool_helper::os_str_to_string(Error::ok_or_new(cmd.output_file.extension(), ||"File extension required for output".to_owned())?, "extension")?.to_ascii_lowercase();
|
let extension = tool_helper::os_str_to_string(Error::ok_or_new(cmd.output_file.extension(), ||"File extension required for output".to_owned())?, "extension")?.to_ascii_lowercase();
|
||||||
let file_type = Error::ok_or_new({
|
let file_type = Error::ok_or_new({
|
||||||
match extension.as_ref() {
|
match extension.as_ref() {
|
||||||
"h" => Some(FileType::CHeader),
|
"h" => Some(FileType::CHeader),
|
||||||
"hpp" => Some(FileType::CPPHeader),
|
"hpp" => Some(FileType::CPPHeader),
|
||||||
"c" => Some(FileType::CSource),
|
"c" => Some(FileType::CSource),
|
||||||
"cpp" => Some(FileType::CPPSource),
|
"cpp" => Some(FileType::CPPSource),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}, ||format!("{} unkown file extension", extension))?;
|
}, ||format!("{} unkown file extension", extension))?;
|
||||||
|
|
||||||
Ok(Configuration{file_name, data_name: std::mem::take(&mut cmd.data_name), line_feed: cpp_out::LineFeed::Windows, file_type})
|
Ok(Configuration{file_name, data_name: std::mem::take(&mut cmd.data_name), line_feed: cpp_out::LineFeed::Windows, file_type})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(mut cmd: CommandLine) -> Result<(), Error> {
|
fn run_main(mut cmd: CommandLine) -> Result<(), Error> {
|
||||||
let cfg = configurate(&mut cmd)?;
|
let cfg = configurate(&mut cmd)?;
|
||||||
let input = tool_helper::open_input(cmd.input_file)?;
|
let input = tool_helper::open_input(cmd.input_file)?;
|
||||||
let output = tool_helper::open_output(Some(cmd.output_file))?;
|
let output = tool_helper::open_output(&Some(cmd.output_file))?;
|
||||||
|
|
||||||
return cpp_out::convert(cfg, input, output);
|
return cpp_out::convert(cfg, input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match CommandLine::try_parse() {
|
match CommandLine::try_parse() {
|
||||||
Ok(cmd) => {
|
Ok(cmd) => {
|
||||||
if let Err(error) = run_main(cmd) {
|
if let Err(error) = run_main(cmd) {
|
||||||
exit_with_error(error)
|
exit_with_error(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => println!("{}", error)
|
Err(error) => println!("{}", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "jaby_engine_fconv"
|
name = "jaby_engine_fconv"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = {version = "*", features = ["derive"]}
|
clap = {version = "*", features = ["derive"]}
|
||||||
image = "*"
|
image = "*"
|
||||||
paste = "*"
|
paste = "*"
|
||||||
png = "*"
|
png = "*"
|
||||||
tool_helper = {path = "../tool_helper"}
|
tool_helper = {path = "../tool_helper"}
|
|
@ -64,9 +64,7 @@ fn modify_palette(mut image: IndexedImage, clut_align: ClutAlignment, semi_trans
|
||||||
fn encode<T: PSXImageConverter>(image: T, color_depth: ColorType, clut_align: ClutAlignment, output: &mut dyn Write) -> Result<(), Error> {
|
fn encode<T: PSXImageConverter>(image: T, color_depth: ColorType, clut_align: ClutAlignment, output: &mut dyn Write) -> Result<(), Error> {
|
||||||
let (width, height) = {
|
let (width, height) = {
|
||||||
fn return_error(clut_type: u32, div: u32, width: u16, height: u16) -> Result<(u16, u16), Error> {
|
fn return_error(clut_type: u32, div: u32, width: u16, height: u16) -> Result<(u16, u16), Error> {
|
||||||
return Err(Error::from_callback(|| {format!("CLUT {} images require a width divideable by {} (found width: {}/{}={}, height: {}/{}={})", clut_type, div,
|
return Err(Error::from_callback(|| {format!("CLUT {} images require a width divideable by {} (found width: {}/{}={}, height: {})", clut_type, div, width, div, (width as f32/div as f32), height)}));
|
||||||
width, div, (width as f32/div as f32),
|
|
||||||
height, div, (height as f32/div as f32))}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = image.width();
|
let width = image.width();
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod images;
|
pub mod images;
|
||||||
|
pub mod nothing;
|
|
@ -1,5 +1,5 @@
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use jaby_engine_fconv::images::*;
|
use jaby_engine_fconv::{images::*, nothing};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tool_helper::{Error, exit_with_error};
|
use tool_helper::{Error, exit_with_error};
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ enum SubCommands {
|
||||||
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
||||||
let mut input = tool_helper::open_input(cmd.input_file)?;
|
let mut input = tool_helper::open_input(cmd.input_file)?;
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
let mut output_file = tool_helper::open_output(cmd.output_file)?;
|
let mut output_file = tool_helper::open_output(&cmd.output_file)?;
|
||||||
let dst_buffer = {
|
let dst_buffer = {
|
||||||
if cmd.compress_lz4 {
|
if cmd.compress_lz4 {
|
||||||
&mut buffer as &mut dyn std::io::Write
|
&mut buffer as &mut dyn std::io::Write
|
||||||
|
@ -39,13 +39,23 @@ fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match cmd.sub_command {
|
let cmd_result: Result<(), Error> = {
|
||||||
SubCommands::Nothing => {
|
match cmd.sub_command {
|
||||||
std::io::copy(&mut input, dst_buffer)?;
|
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
|
||||||
},
|
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer)
|
||||||
SubCommands::SimpleTIM(args) => {
|
|
||||||
reduced_tim::convert(args, input, dst_buffer)?;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(cmd_error) = cmd_result {
|
||||||
|
if let Some(file_path) = cmd.output_file {
|
||||||
|
let _result = std::fs::remove_file(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
tool_helper::print_warning("Open stream detected! Incomplete file can not be deleted".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(cmd_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We encoded the file to a temporary buffer and now need to write it
|
// We encoded the file to a temporary buffer and now need to write it
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use tool_helper::{Error, Input};
|
||||||
|
|
||||||
|
pub fn copy(input: &mut Input, output: &mut dyn Write) -> Result<(), Error> {
|
||||||
|
std::io::copy(input, output)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,51 +1,51 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mkoverlay::types::OverlayDesc;
|
use mkoverlay::types::OverlayDesc;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tool_helper::{Error, exit_with_error, format_if_error, open_input, open_output};
|
use tool_helper::{Error, exit_with_error, format_if_error, open_input, open_output};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(about = "Creates a linker script and makefile part for the JabyEngine toolchain", long_about = None)]
|
#[clap(about = "Creates a linker script and makefile part for the JabyEngine toolchain", long_about = None)]
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
#[clap(long="mk-file", help="Output path for the makefile")]
|
#[clap(long="mk-file", help="Output path for the makefile")]
|
||||||
mk_file_output: Option<PathBuf>,
|
mk_file_output: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(long="ld-script", help="Output path for the linker script file")]
|
#[clap(long="ld-script", help="Output path for the linker script file")]
|
||||||
ld_file_output: Option<PathBuf>,
|
ld_file_output: Option<PathBuf>,
|
||||||
|
|
||||||
#[clap(value_parser, help="Input JSON for creating the files")]
|
#[clap(value_parser, help="Input JSON for creating the files")]
|
||||||
input: Option<PathBuf>
|
input: Option<PathBuf>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_input(input: Option<PathBuf>) -> Result<OverlayDesc, Error> {
|
fn parse_input(input: Option<PathBuf>) -> Result<OverlayDesc, Error> {
|
||||||
if let Some(input) = input {
|
if let Some(input) = input {
|
||||||
let input = format_if_error!(open_input(Some(input)), "Opening input file failed with: {error_text}")?;
|
let input = format_if_error!(open_input(Some(input)), "Opening input file failed with: {error_text}")?;
|
||||||
format_if_error!(mkoverlay::types::json_reader::read_config(input), "Parsing JSON file failed with: {error_text}")
|
format_if_error!(mkoverlay::types::json_reader::read_config(input), "Parsing JSON file failed with: {error_text}")
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
Ok(OverlayDesc::new())
|
Ok(OverlayDesc::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(cmd_line: CommandLine) -> Result<(), Error> {
|
fn run_main(cmd_line: CommandLine) -> Result<(), Error> {
|
||||||
let input = parse_input(cmd_line.input)?;
|
let input = parse_input(cmd_line.input)?;
|
||||||
let mut mk_output = format_if_error!(open_output(cmd_line.mk_file_output), "Opening file for writing makefile failed with: {error_text}")?;
|
let mut mk_output = format_if_error!(open_output(&cmd_line.mk_file_output), "Opening file for writing makefile failed with: {error_text}")?;
|
||||||
let mut ld_output = format_if_error!(open_output(cmd_line.ld_file_output), "Opening file for writing linkerfile failed with: {error_text}")?;
|
let mut ld_output = format_if_error!(open_output(&cmd_line.ld_file_output), "Opening file for writing linkerfile failed with: {error_text}")?;
|
||||||
|
|
||||||
format_if_error!(mkoverlay::creator::makefile::write(&mut mk_output, &input), "Writing makefile failed with: {error_text}")?;
|
format_if_error!(mkoverlay::creator::makefile::write(&mut mk_output, &input), "Writing makefile failed with: {error_text}")?;
|
||||||
format_if_error!(mkoverlay::creator::ldscript::write(&mut ld_output, &input), "Writing ld script failed with: {error_text}")
|
format_if_error!(mkoverlay::creator::ldscript::write(&mut ld_output, &input), "Writing ld script failed with: {error_text}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match CommandLine::try_parse() {
|
match CommandLine::try_parse() {
|
||||||
Ok(cmd_line) => {
|
Ok(cmd_line) => {
|
||||||
match run_main(cmd_line) {
|
match run_main(cmd_line) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(error) => exit_with_error(error)
|
Err(error) => exit_with_error(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("{}", error)
|
println!("{}", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,469 +1,468 @@
|
||||||
use super::{*, SectorWriter, {CDDesc, Error}};
|
use super::{*, SectorWriter, {CDDesc, Error}};
|
||||||
use super::super::types::{helper::{DirectoryRecordMember, PathTableMember}, layout::Layout, *};
|
use super::super::types::{helper::{DirectoryRecordMember, PathTableMember}, layout::Layout, *};
|
||||||
use builder::SubModeBuilder;
|
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}, path_table::*, pvd as cd_pvd, lsb_msb::*, sector::Mode2Form1};
|
||||||
use colored::*;
|
use tool_helper::{BufferedInputFile, format_if_error, open_input_file_buffered, print_warning};
|
||||||
use tool_helper::{BufferedInputFile, format_if_error, open_input_file_buffered};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
|
||||||
|
const ROOT_DIR_NAME:&'static str = "\x00";
|
||||||
const ROOT_DIR_NAME:&'static str = "\x00";
|
const SYSTEM_AREA_SECTOR_COUNT:usize = 16;
|
||||||
const SYSTEM_AREA_SECTOR_COUNT:usize = 16;
|
const PVD_SECTOR_COUNT:usize = 2;
|
||||||
const PVD_SECTOR_COUNT:usize = 2;
|
|
||||||
|
pub fn calculate_psx_length_for(element: &Layout) -> LengthInfo {
|
||||||
pub fn calculate_psx_length_for(element: &Layout) -> LengthInfo {
|
match element {
|
||||||
match element {
|
Layout::SystemArea(_) => LengthInfo{bytes: None, sectors: SYSTEM_AREA_SECTOR_COUNT},
|
||||||
Layout::SystemArea(_) => LengthInfo{bytes: None, sectors: SYSTEM_AREA_SECTOR_COUNT},
|
Layout::PVD(_) => LengthInfo{bytes: None, sectors: PVD_SECTOR_COUNT},
|
||||||
Layout::PVD(_) => LengthInfo{bytes: None, sectors: PVD_SECTOR_COUNT},
|
Layout::PathTables(root, _) => {
|
||||||
Layout::PathTables(root, _) => {
|
let path_table_size = PathTable::calculate_size_for(root.clone());
|
||||||
let path_table_size = PathTable::calculate_size_for(root.clone());
|
LengthInfo{bytes: Some(path_table_size), sectors: sector_count_mode2_form1(path_table_size)}
|
||||||
LengthInfo{bytes: Some(path_table_size), sectors: sector_count_mode2_form1(path_table_size)}
|
},
|
||||||
},
|
Layout::Directory(dir) => {
|
||||||
Layout::Directory(dir) => {
|
let dir = dir.borrow();
|
||||||
let dir = dir.borrow();
|
let properties = dir.properties.borrow_mut();
|
||||||
let properties = dir.properties.borrow_mut();
|
let size_bytes = properties.get_padded_size();
|
||||||
let size_bytes = properties.get_padded_size();
|
|
||||||
|
return LengthInfo{bytes: Some(size_bytes), sectors: sector_count_mode2_form1(size_bytes)};
|
||||||
return LengthInfo{bytes: Some(size_bytes), sectors: sector_count_mode2_form1(size_bytes)};
|
},
|
||||||
},
|
Layout::File(file) => {
|
||||||
Layout::File(file) => {
|
let file = file.borrow();
|
||||||
let file = file.borrow();
|
let fake_size = file.properties.get_padded_size();
|
||||||
let fake_size = file.properties.get_padded_size();
|
|
||||||
|
return LengthInfo{bytes: Some(fake_size), sectors: sector_count_mode2_form1(fake_size)};
|
||||||
return LengthInfo{bytes: Some(fake_size), sectors: sector_count_mode2_form1(fake_size)};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub fn calculate_psx_lbas(cd_desc: &mut CDDesc) {
|
||||||
pub fn calculate_psx_lbas(cd_desc: &mut CDDesc) {
|
let mut cur_lba = 0;
|
||||||
let mut cur_lba = 0;
|
let track_offset = LBA::FIRST_TRACK_OFFSET;
|
||||||
let track_offset = LBA::FIRST_TRACK_OFFSET;
|
|
||||||
|
cd_desc.vol_sector_count = 0;
|
||||||
cd_desc.vol_sector_count = 0;
|
CDDesc::for_each_dir_mut(cd_desc.root.clone(), &|dir| {dir.update_content_size();});
|
||||||
CDDesc::for_each_dir_mut(cd_desc.root.clone(), &|dir| {dir.update_content_size();});
|
|
||||||
|
for element in cd_desc.get_memory_layout() {
|
||||||
for element in cd_desc.get_memory_layout() {
|
fn update_lba(properties: &mut Properties, cur_lba: usize, track_offset: usize, content_sector_size: usize) -> usize {
|
||||||
fn update_lba(properties: &mut Properties, cur_lba: usize, track_offset: usize, content_sector_size: usize) -> usize {
|
properties.lba.overwrite(cur_lba, track_offset);
|
||||||
properties.lba.overwrite(cur_lba, track_offset);
|
cur_lba + content_sector_size
|
||||||
cur_lba + content_sector_size
|
}
|
||||||
}
|
|
||||||
|
let element_size_info = calculate_psx_length_for(&element);
|
||||||
let element_size_info = calculate_psx_length_for(&element);
|
match element {
|
||||||
match element {
|
Layout::SystemArea(system_area) => {
|
||||||
Layout::SystemArea(system_area) => {
|
let mut system_area = system_area.borrow_mut();
|
||||||
let mut system_area = system_area.borrow_mut();
|
|
||||||
|
system_area.lba.overwrite(cur_lba, track_offset);
|
||||||
system_area.lba.overwrite(cur_lba, track_offset);
|
cur_lba += element_size_info.sectors;
|
||||||
cur_lba += element_size_info.sectors;
|
},
|
||||||
},
|
|
||||||
|
Layout::PVD(pvd) => {
|
||||||
Layout::PVD(pvd) => {
|
let mut pvd = pvd.borrow_mut();
|
||||||
let mut pvd = pvd.borrow_mut();
|
|
||||||
|
pvd.lba.overwrite(cur_lba, track_offset);
|
||||||
pvd.lba.overwrite(cur_lba, track_offset);
|
cur_lba += element_size_info.sectors;
|
||||||
cur_lba += element_size_info.sectors;
|
},
|
||||||
},
|
|
||||||
|
Layout::PathTables(_, path_table) => {
|
||||||
Layout::PathTables(_, path_table) => {
|
let mut path_table = path_table.borrow_mut();
|
||||||
let mut path_table = path_table.borrow_mut();
|
|
||||||
|
path_table.lba.overwrite(cur_lba, track_offset);
|
||||||
path_table.lba.overwrite(cur_lba, track_offset);
|
path_table.size_bytes = element_size_info.bytes.unwrap_or(0);
|
||||||
path_table.size_bytes = element_size_info.bytes.unwrap_or(0);
|
|
||||||
|
cur_lba += element_size_info.sectors*4;
|
||||||
cur_lba += element_size_info.sectors*4;
|
},
|
||||||
},
|
|
||||||
|
Layout::Directory(dir) => {
|
||||||
Layout::Directory(dir) => {
|
let dir = dir.borrow_mut();
|
||||||
let dir = dir.borrow_mut();
|
let mut properties = dir.properties.borrow_mut();
|
||||||
let mut properties = dir.properties.borrow_mut();
|
|
||||||
|
cd_desc.vol_sector_count += element_size_info.sectors;
|
||||||
cd_desc.vol_sector_count += element_size_info.sectors;
|
if properties.is_hidden {
|
||||||
if properties.is_hidden {
|
properties.lba.overwrite(0, 0);
|
||||||
properties.lba.overwrite(0, 0);
|
}
|
||||||
}
|
|
||||||
|
else {
|
||||||
else {
|
cur_lba = update_lba(&mut properties, cur_lba, track_offset, element_size_info.sectors);
|
||||||
cur_lba = update_lba(&mut properties, cur_lba, track_offset, element_size_info.sectors);
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
Layout::File(file) => {
|
||||||
Layout::File(file) => {
|
let mut file = file.borrow_mut();
|
||||||
let mut file = file.borrow_mut();
|
|
||||||
|
cd_desc.vol_sector_count += element_size_info.sectors;
|
||||||
cd_desc.vol_sector_count += element_size_info.sectors;
|
cur_lba = update_lba(&mut file.properties, cur_lba, track_offset, element_size_info.sectors);
|
||||||
cur_lba = update_lba(&mut file.properties, cur_lba, track_offset, element_size_info.sectors);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
pub fn encode_psx_image(cd_desc: &CDDesc, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
pub fn encode_psx_image(cd_desc: &CDDesc, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
let vol_sector_count = cd_desc.vol_sector_count;
|
||||||
let vol_sector_count = cd_desc.vol_sector_count;
|
|
||||||
|
for element in cd_desc.get_memory_layout() {
|
||||||
for element in cd_desc.get_memory_layout() {
|
match element {
|
||||||
match element {
|
Layout::SystemArea(system_area) => process_system_area(&mut system_area.borrow_mut(), sec_writer)?,
|
||||||
Layout::SystemArea(system_area) => process_system_area(&mut system_area.borrow_mut(), sec_writer)?,
|
Layout::PVD(pvd) => process_pvd(&pvd.borrow(), cd_desc.path_table.clone(), cd_desc.root.clone(), vol_sector_count, sec_writer)?,
|
||||||
Layout::PVD(pvd) => process_pvd(&pvd.borrow(), cd_desc.path_table.clone(), cd_desc.root.clone(), vol_sector_count, sec_writer)?,
|
Layout::PathTables(root, path_table) => process_path_table(&path_table.borrow(), root, sec_writer)?,
|
||||||
Layout::PathTables(root, path_table) => process_path_table(&path_table.borrow(), root, sec_writer)?,
|
Layout::Directory(dir) => process_directory_record(&dir.borrow(), sec_writer)?,
|
||||||
Layout::Directory(dir) => process_directory_record(&dir.borrow(), sec_writer)?,
|
Layout::File(file) => process_file(&file.borrow(), sec_writer)?,
|
||||||
Layout::File(file) => process_file(&file.borrow(), sec_writer)?,
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn process_system_area(system_area: &SystemArea, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn process_system_area(system_area: &SystemArea, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
fn write_license_file(sec_writer: &mut dyn SectorWriter, mut license_file: BufferedInputFile) -> Result<(), Error> {
|
||||||
fn write_license_file(sec_writer: &mut dyn SectorWriter, mut license_file: BufferedInputFile) -> Result<(), Error> {
|
fn write_data_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn write_data_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
for _ in 0..4 {
|
||||||
for _ in 0..4 {
|
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn write_license_string(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
|
||||||
fn write_license_string(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
|
const LICENSE_STRING_START:u64 = 0x2488;
|
||||||
const LICENSE_STRING_START:u64 = 0x2488;
|
|
||||||
|
if license_file.get_ref().metadata()?.len() < LICENSE_STRING_START {
|
||||||
if license_file.get_ref().metadata()?.len() < LICENSE_STRING_START {
|
return Err(Error::from_str("License file to short to contain license string. Is this is a valid license file?"));
|
||||||
return Err(Error::from_str("License file to short to contain license string. Is this is a valid license file?"));
|
}
|
||||||
}
|
|
||||||
|
let mut license_string_buffer = [0u8; Mode2Form1::DATA_SIZE];
|
||||||
let mut license_string_buffer = [0u8; Mode2Form1::DATA_SIZE];
|
|
||||||
|
license_file.seek(SeekFrom::Start(LICENSE_STRING_START))?;
|
||||||
license_file.seek(SeekFrom::Start(LICENSE_STRING_START))?;
|
license_file.read(&mut license_string_buffer)?;
|
||||||
license_file.read(&mut license_string_buffer)?;
|
|
||||||
|
let sub_mode = SubModeBuilder::new_mode1().create();
|
||||||
let sub_mode = SubModeBuilder::new_mode1().create();
|
let sector = builder::create_xa_data_for_raw(sub_mode, &license_string_buffer);
|
||||||
let sector = builder::create_xa_data_for_raw(sub_mode, &license_string_buffer);
|
|
||||||
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn write_license_logo(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
|
||||||
fn write_license_logo(sec_writer: &mut dyn SectorWriter, license_file: &mut BufferedInputFile) -> Result<(), Error> {
|
const LICENSE_LOGO_START:u64 = 0x2DA8;
|
||||||
const LICENSE_LOGO_START:u64 = 0x2DA8;
|
|
||||||
|
license_file.seek(SeekFrom::Start(LICENSE_LOGO_START))?;
|
||||||
license_file.seek(SeekFrom::Start(LICENSE_LOGO_START))?;
|
for _ in 0..7 {
|
||||||
for _ in 0..7 {
|
const LICENSE_SECTOR_REST:i64 = 0x120;
|
||||||
const LICENSE_SECTOR_REST:i64 = 0x120;
|
|
||||||
|
if license_file.get_ref().metadata()?.len() < license_file.stream_position()? + LICENSE_SECTOR_REST as u64 {
|
||||||
if license_file.get_ref().metadata()?.len() < license_file.stream_position()? + LICENSE_SECTOR_REST as u64 {
|
return Err(Error::from_str("License file to short to contain license logo. Is this is a valid license file?"));
|
||||||
return Err(Error::from_str("License file to short to contain license logo. Is this is a valid license file?"));
|
}
|
||||||
}
|
|
||||||
|
let mut license_logo_buffer = [0u8; Mode2Form1::DATA_SIZE];
|
||||||
let mut license_logo_buffer = [0u8; Mode2Form1::DATA_SIZE];
|
|
||||||
|
license_file.read(&mut license_logo_buffer)?;
|
||||||
license_file.read(&mut license_logo_buffer)?;
|
license_file.seek(SeekFrom::Current(LICENSE_SECTOR_REST))?;
|
||||||
license_file.seek(SeekFrom::Current(LICENSE_SECTOR_REST))?;
|
|
||||||
|
let sub_mode = SubModeBuilder::new_mode1().create();
|
||||||
let sub_mode = SubModeBuilder::new_mode1().create();
|
let sector = builder::create_xa_data_for_raw(sub_mode, &license_logo_buffer);
|
||||||
let sector = builder::create_xa_data_for_raw(sub_mode, &license_logo_buffer);
|
|
||||||
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
}
|
||||||
}
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn write_audio_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn write_audio_zeros(sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
for _ in 0..4 {
|
||||||
for _ in 0..4 {
|
let sector = {
|
||||||
let sector = {
|
let mut sector = builder::create_xa_audio_zero();
|
||||||
let mut sector = builder::create_xa_audio_zero();
|
|
||||||
|
sector.sub_header.sub_mode.clear_audio();
|
||||||
sector.sub_header.sub_mode.clear_audio();
|
sector
|
||||||
sector
|
};
|
||||||
};
|
sec_writer.write_cd_xa_audio(sector)?;
|
||||||
sec_writer.write_cd_xa_audio(sector)?;
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
format_if_error!(write_data_zeros(sec_writer), "Writing license data zeros failed with: {error_text}")?;
|
||||||
format_if_error!(write_data_zeros(sec_writer), "Writing license data zeros failed with: {error_text}")?;
|
format_if_error!(write_license_string(sec_writer, &mut license_file), "Writing license string from file failed with: {error_text}")?;
|
||||||
format_if_error!(write_license_string(sec_writer, &mut license_file), "Writing license string from file failed with: {error_text}")?;
|
format_if_error!(write_license_logo(sec_writer, &mut license_file), "Writing license logo from file failed with: {error_text}")?;
|
||||||
format_if_error!(write_license_logo(sec_writer, &mut license_file), "Writing license logo from file failed with: {error_text}")?;
|
format_if_error!(write_audio_zeros(sec_writer), "Writing license audio zeros failed with: {error_text}")?;
|
||||||
format_if_error!(write_audio_zeros(sec_writer), "Writing license audio zeros failed with: {error_text}")?;
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
let system_area_lba = system_area.lba.get_track_relative();
|
||||||
let system_area_lba = system_area.lba.get_track_relative();
|
if system_area_lba != 0 {
|
||||||
if system_area_lba != 0 {
|
return Err(Error::from_text(format!("System Area required to start at sector 0 of Track - found LBA: {}", system_area_lba)));
|
||||||
return Err(Error::from_text(format!("System Area required to start at sector 0 of Track - found LBA: {}", system_area_lba)));
|
}
|
||||||
}
|
|
||||||
|
if let Some(license_path) = &system_area.license_file_path {
|
||||||
if let Some(license_path) = &system_area.license_file_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())?;
|
||||||
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)
|
||||||
write_license_file(sec_writer, license_file)
|
}
|
||||||
}
|
|
||||||
|
else {
|
||||||
else {
|
// No license specified - filling it with zeros
|
||||||
// No license specified - filling it with zeros
|
print_warning("WARNING: No license file provided. Some emulators (like No$PSX) will not boot this CD.".to_owned());
|
||||||
eprintln!("{}", "WARNING: No license file provided. Some emulators (like No$PSX) will not boot this CD.".yellow());
|
for _ in 0..SYSTEM_AREA_SECTOR_COUNT {
|
||||||
for _ in 0..SYSTEM_AREA_SECTOR_COUNT {
|
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fn process_pvd(pvd: &PrimaryVolumeDescriptor, path_table: SharedPtr<PathTable>, root_dir: SharedPtr<Directory>, vol_sector_count: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn process_pvd(pvd: &PrimaryVolumeDescriptor, path_table: SharedPtr<PathTable>, root_dir: SharedPtr<Directory>, vol_sector_count: usize, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
const PLAYSATATION_STR:&'static str = "PLAYSTATION";
|
||||||
const PLAYSATATION_STR:&'static str = "PLAYSTATION";
|
|
||||||
|
let path_table = validate_and_unwrap_path_table(&path_table)?;
|
||||||
let path_table = validate_and_unwrap_path_table(&path_table)?;
|
let root_dir = root_dir.borrow();
|
||||||
let root_dir = root_dir.borrow();
|
let pvd_lba = pvd.lba.get_track_relative();
|
||||||
let pvd_lba = pvd.lba.get_track_relative();
|
|
||||||
|
if pvd_lba != 16 {
|
||||||
if pvd_lba != 16 {
|
return Err(Error::from_text(format!("PVD required to start at sector 16 of Track - found LBA: {}", pvd_lba)));
|
||||||
return Err(Error::from_text(format!("PVD required to start at sector 16 of Track - found LBA: {}", pvd_lba)));
|
}
|
||||||
}
|
|
||||||
|
let mut cd_pvd = cd_pvd::PrimaryVolumeDescriptor::new();
|
||||||
let mut cd_pvd = cd_pvd::PrimaryVolumeDescriptor::new();
|
let now = Date::now();
|
||||||
let now = Date::now();
|
|
||||||
|
//Config pvd here
|
||||||
//Config pvd here
|
cd_pvd.system_id = AString::from_str(PLAYSATATION_STR)?;
|
||||||
cd_pvd.system_id = AString::from_str(PLAYSATATION_STR)?;
|
cd_pvd.volume_id = DString::from_str("PSX")?;
|
||||||
cd_pvd.volume_id = DString::from_str("PSX")?;
|
cd_pvd.vol_space_size.write(vol_sector_count as u32);
|
||||||
cd_pvd.vol_space_size.write(vol_sector_count as u32);
|
|
||||||
|
//Set PathTable values
|
||||||
//Set PathTable values
|
cd_pvd.path_table_size.write(path_table.size_bytes as u32);
|
||||||
cd_pvd.path_table_size.write(path_table.size_bytes as u32);
|
cd_pvd.path_table_1.write(path_table.get_track_rel_lba_for(1, sector_count_mode2_form1) as u32);
|
||||||
cd_pvd.path_table_1.write(path_table.get_track_rel_lba_for(1, sector_count_mode2_form1) as u32);
|
cd_pvd.path_table_2.write(path_table.get_track_rel_lba_for(2, sector_count_mode2_form1) as u32);
|
||||||
cd_pvd.path_table_2.write(path_table.get_track_rel_lba_for(2, sector_count_mode2_form1) as u32);
|
cd_pvd.path_table_3.write(path_table.get_track_rel_lba_for(3, sector_count_mode2_form1) as u32);
|
||||||
cd_pvd.path_table_3.write(path_table.get_track_rel_lba_for(3, sector_count_mode2_form1) as u32);
|
cd_pvd.path_table_4.write(path_table.get_track_rel_lba_for(4, sector_count_mode2_form1) as u32);
|
||||||
cd_pvd.path_table_4.write(path_table.get_track_rel_lba_for(4, sector_count_mode2_form1) as u32);
|
|
||||||
|
//Set Root Directory Record
|
||||||
//Set Root Directory Record
|
write_dir_record(&mut cd_pvd.root_dir_record, &DirectoryRecordMember::new_dir(ROOT_DIR_NAME.to_owned(), &root_dir.properties.borrow()), false)?;
|
||||||
write_dir_record(&mut cd_pvd.root_dir_record, &DirectoryRecordMember::new_dir(ROOT_DIR_NAME.to_owned(), &root_dir.properties.borrow()), false)?;
|
|
||||||
|
//Set other stuff
|
||||||
//Set other stuff
|
cd_pvd.publisher_id = AString::from_str(pvd.publisher.as_str())?;
|
||||||
cd_pvd.publisher_id = AString::from_str(pvd.publisher.as_str())?;
|
cd_pvd.data_preparer = AString::from_str("JABYENGINE PSXCDGEN_EX")?;
|
||||||
cd_pvd.data_preparer = AString::from_str("JABYENGINE PSXCDGEN_EX")?;
|
cd_pvd.app_id = AString::from_str(PLAYSATATION_STR)?;
|
||||||
cd_pvd.app_id = AString::from_str(PLAYSATATION_STR)?;
|
|
||||||
|
cd_pvd.vol_create_time = now;
|
||||||
cd_pvd.vol_create_time = now;
|
|
||||||
|
cd_pvd.cd_xa_id = ['C' as u8, 'D' as u8, '-' as u8, 'X' as u8, 'A' as u8, '0' as u8, '0' as u8, '1' as u8];
|
||||||
cd_pvd.cd_xa_id = ['C' as u8, 'D' as u8, '-' as u8, 'X' as u8, 'A' as u8, '0' as u8, '0' as u8, '1' as u8];
|
|
||||||
|
//Write PVD and VDT
|
||||||
//Write PVD and VDT
|
sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().create(), &cd_pvd))?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().create(), &cd_pvd))?;
|
sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().set_eof().create(), &cd_pvd::VolumeDescriptorTerminator::new()))?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_for(SubModeBuilder::new_mode1().set_eor().set_eof().create(), &cd_pvd::VolumeDescriptorTerminator::new()))?;
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn process_path_table(path_table: &PathTable, root_dir: SharedPtr<Directory>, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn process_path_table(path_table: &PathTable, root_dir: SharedPtr<Directory>, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
macro_rules! write_path_table_twice {
|
||||||
macro_rules! write_path_table_twice {
|
($table:ident) => {
|
||||||
($table:ident) => {
|
for sector in $table.clone() {
|
||||||
for sector in $table.clone() {
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
}
|
||||||
}
|
|
||||||
|
for sector in $table {
|
||||||
for sector in $table {
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
}
|
||||||
}
|
};
|
||||||
};
|
}
|
||||||
}
|
let mut bytes_used = 0;
|
||||||
let mut bytes_used = 0;
|
let mut path_table_raw_l = vec![0u8; path_table.size_bytes];
|
||||||
let mut path_table_raw_l = vec![0u8; path_table.size_bytes];
|
let mut path_table_raw_b = vec![0u8; path_table.size_bytes];
|
||||||
let mut path_table_raw_b = vec![0u8; path_table.size_bytes];
|
|
||||||
|
validate_path_table(path_table)?;
|
||||||
validate_path_table(path_table)?;
|
let path_table = PathTable::collect_member(root_dir);
|
||||||
let path_table = PathTable::collect_member(root_dir);
|
|
||||||
|
for entry in path_table {
|
||||||
for entry in path_table {
|
bytes_used += unsafe{update_path_table_entry(std::mem::transmute::<&mut u8, &mut PathTableL>(&mut path_table_raw_l[bytes_used]), std::mem::transmute::<&mut u8, &mut PathTableB>(&mut path_table_raw_b[bytes_used]), entry)?};
|
||||||
bytes_used += unsafe{update_path_table_entry(std::mem::transmute::<&mut u8, &mut PathTableL>(&mut path_table_raw_l[bytes_used]), std::mem::transmute::<&mut u8, &mut PathTableB>(&mut path_table_raw_b[bytes_used]), entry)?};
|
}
|
||||||
}
|
|
||||||
|
let path_table_l = builder::create_xa_data_for_vec(None, &path_table_raw_l);
|
||||||
let path_table_l = builder::create_xa_data_for_vec(None, &path_table_raw_l);
|
let path_table_b = builder::create_xa_data_for_vec(None, &path_table_raw_b);
|
||||||
let path_table_b = builder::create_xa_data_for_vec(None, &path_table_raw_b);
|
|
||||||
|
write_path_table_twice!(path_table_l);
|
||||||
write_path_table_twice!(path_table_l);
|
write_path_table_twice!(path_table_b);
|
||||||
write_path_table_twice!(path_table_b);
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn process_directory_record(dir: &Directory, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn process_directory_record(dir: &Directory, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
if dir.properties.borrow().is_hidden {
|
||||||
if dir.properties.borrow().is_hidden {
|
return Ok(());
|
||||||
return Ok(());
|
}
|
||||||
}
|
|
||||||
|
let properties = dir.properties.borrow();
|
||||||
let properties = dir.properties.borrow();
|
if !properties.is_size_valid() {
|
||||||
if !properties.is_size_valid() {
|
return Err(create_wrong_padding_error("Dir", dir.name.to_string(), properties.get_padded_size(), properties.get_real_size()));
|
||||||
return Err(create_wrong_padding_error("Dir", dir.name.to_string(), properties.get_padded_size(), properties.get_real_size()));
|
}
|
||||||
}
|
|
||||||
|
let mut dir_record = vec![0u8; properties.get_real_size()];
|
||||||
let mut dir_record = vec![0u8; properties.get_real_size()];
|
let dir_length = dir_record.len();
|
||||||
let dir_length = dir_record.len();
|
let mut raw_data = &mut dir_record[0..dir_length];
|
||||||
let mut raw_data = &mut dir_record[0..dir_length];
|
|
||||||
|
for member in dir.collect_member() {
|
||||||
for member in dir.collect_member() {
|
let raw_data_len = raw_data.len();
|
||||||
let raw_data_len = raw_data.len();
|
let bytes_written = write_dir_record(raw_data, &member, true)?;
|
||||||
let bytes_written = write_dir_record(raw_data, &member, true)?;
|
|
||||||
|
raw_data = &mut raw_data[bytes_written..raw_data_len];
|
||||||
raw_data = &mut raw_data[bytes_written..raw_data_len];
|
}
|
||||||
}
|
|
||||||
|
let dir_record_sectors = builder::create_xa_data_for_vec(None, &dir_record);
|
||||||
let dir_record_sectors = builder::create_xa_data_for_vec(None, &dir_record);
|
let dir_record_sector_count = dir_record_sectors.len();
|
||||||
let dir_record_sector_count = dir_record_sectors.len();
|
|
||||||
|
if dir_record_sector_count > 1 {
|
||||||
if dir_record_sector_count > 1 {
|
return Err(Error::from_text(format!("Directory Record for {} spans {} sectors but PSX doesn't support more then one sector", dir.name.as_str().unwrap_or("<No name>"), dir_record_sector_count)));
|
||||||
return Err(Error::from_text(format!("Directory Record for {} spans {} sectors but PSX doesn't support more then one sector", dir.name.as_str().unwrap_or("<No name>"), dir_record_sector_count)));
|
}
|
||||||
}
|
|
||||||
|
for sector in dir_record_sectors {
|
||||||
for sector in dir_record_sectors {
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
}
|
||||||
}
|
|
||||||
|
let extended_sector_count = sector_count_mode2_form1(dir.properties.borrow().get_padded_size()) - dir_record_sector_count;
|
||||||
let extended_sector_count = sector_count_mode2_form1(dir.properties.borrow().get_padded_size()) - dir_record_sector_count;
|
for _ in 0..extended_sector_count {
|
||||||
for _ in 0..extended_sector_count {
|
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn process_file(file: &File, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
||||||
fn process_file(file: &File, sec_writer: &mut dyn SectorWriter) -> Result<(), Error> {
|
if !file.properties.is_size_valid() {
|
||||||
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()));
|
||||||
return Err(create_wrong_padding_error("File", file.name.to_string(), file.properties.get_padded_size(), file.properties.get_real_size()));
|
}
|
||||||
}
|
|
||||||
|
let content_sectors = {
|
||||||
let content_sectors = {
|
match &file.content {
|
||||||
match &file.content {
|
FileType::Regular(raw) => builder::create_xa_data_for_vec(None, raw),
|
||||||
FileType::Regular(raw) => builder::create_xa_data_for_vec(None, raw),
|
FileType::Main(_, _) => {
|
||||||
FileType::Main(_, _) => {
|
return Err(Error::from_str("Trying to encode an unprocssed main file"));
|
||||||
return Err(Error::from_str("Trying to encode an unprocssed main file"));
|
},
|
||||||
},
|
FileType::Overlay(_, _) => {
|
||||||
FileType::Overlay(_, _) => {
|
return Err(Error::from_str("Trying to encode an unprocessed overlay file"));
|
||||||
return Err(Error::from_str("Trying to encode an unprocessed overlay file"));
|
},
|
||||||
},
|
}
|
||||||
}
|
};
|
||||||
};
|
let content_sector_count = content_sectors.len();
|
||||||
let content_sector_count = content_sectors.len();
|
|
||||||
|
for sector in content_sectors {
|
||||||
for sector in content_sectors {
|
sec_writer.write_cd_xa_data(sector)?;
|
||||||
sec_writer.write_cd_xa_data(sector)?;
|
}
|
||||||
}
|
|
||||||
|
let extended_sector_count = sector_count_mode2_form1(file.properties.get_padded_size()) - content_sector_count;
|
||||||
let extended_sector_count = sector_count_mode2_form1(file.properties.get_padded_size()) - content_sector_count;
|
for _ in 0..extended_sector_count {
|
||||||
for _ in 0..extended_sector_count {
|
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
||||||
sec_writer.write_cd_xa_data(builder::create_xa_data_zero())?;
|
}
|
||||||
}
|
|
||||||
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
|
||||||
|
fn validate_path_table(path_table: &PathTable) -> Result<(), Error> {
|
||||||
fn validate_path_table(path_table: &PathTable) -> Result<(), Error> {
|
if path_table.size_bytes > Mode2Form1::DATA_SIZE {
|
||||||
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)))
|
||||||
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)))
|
}
|
||||||
}
|
|
||||||
|
else {
|
||||||
else {
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fn validate_and_unwrap_path_table(path_table: &SharedPtr<PathTable>) -> Result<std::cell::Ref<PathTable>, Error> {
|
||||||
fn validate_and_unwrap_path_table(path_table: &SharedPtr<PathTable>) -> Result<std::cell::Ref<PathTable>, Error> {
|
let path_table = path_table.borrow();
|
||||||
let path_table = path_table.borrow();
|
|
||||||
|
validate_path_table(&path_table)?;
|
||||||
validate_path_table(&path_table)?;
|
Ok(path_table)
|
||||||
Ok(path_table)
|
}
|
||||||
}
|
|
||||||
|
fn update_path_table_entry(path_table_l: &mut PathTableL, path_table_b: &mut PathTableB, entry: PathTableMember) -> Result<usize, Error> {
|
||||||
fn update_path_table_entry(path_table_l: &mut PathTableL, path_table_b: &mut PathTableB, entry: PathTableMember) -> Result<usize, Error> {
|
let name_len = entry.name.len();
|
||||||
let name_len = entry.name.len();
|
if name_len > 8 {
|
||||||
if name_len > 8 {
|
return Err(Error::from_text(format!("Directory name can not exceed size of 8 characters but folder {} has {} characters", entry.name, name_len)));
|
||||||
return Err(Error::from_text(format!("Directory name can not exceed size of 8 characters but folder {} has {} characters", entry.name, name_len)));
|
}
|
||||||
}
|
|
||||||
|
unsafe{
|
||||||
unsafe{
|
let name = entry.name.as_str();
|
||||||
let name = entry.name.as_str();
|
|
||||||
|
path_table_l.new(name);
|
||||||
path_table_l.new(name);
|
path_table_b.new(name);
|
||||||
path_table_b.new(name);
|
}
|
||||||
}
|
|
||||||
|
path_table_l.directory_logical_block.write(entry.track_rel_lba as u32);
|
||||||
path_table_l.directory_logical_block.write(entry.track_rel_lba as u32);
|
path_table_b.directory_logical_block.write(entry.track_rel_lba as u32);
|
||||||
path_table_b.directory_logical_block.write(entry.track_rel_lba as u32);
|
path_table_l.parent_table_id.write(entry.parent_table_id as u16);
|
||||||
path_table_l.parent_table_id.write(entry.parent_table_id as u16);
|
path_table_b.parent_table_id.write(entry.parent_table_id as u16);
|
||||||
path_table_b.parent_table_id.write(entry.parent_table_id as u16);
|
|
||||||
|
Ok(path_table_l.get_size())
|
||||||
Ok(path_table_l.get_size())
|
}
|
||||||
}
|
|
||||||
|
fn create_dir_record_raw<'a>(dst: &'a mut [u8], name: &str, track_rel_lba: u32, size_bytes: u32, system_use: Option<CDXASystemUse>) -> Result<&'a mut DirectoryRecord, Error> {
|
||||||
fn create_dir_record_raw<'a>(dst: &'a mut [u8], name: &str, track_rel_lba: u32, size_bytes: u32, system_use: Option<CDXASystemUse>) -> Result<&'a mut DirectoryRecord, Error> {
|
let has_system_use = system_use.is_some();
|
||||||
let has_system_use = system_use.is_some();
|
let bytes_needed = DirectoryRecord::calculate_size_for(name, has_system_use);
|
||||||
let bytes_needed = DirectoryRecord::calculate_size_for(name, has_system_use);
|
|
||||||
|
if dst.len() < bytes_needed {
|
||||||
if dst.len() < bytes_needed {
|
return Err(Error::from_text(format!("DirectoryRecord for entry {} needs {} bytes but {} bytes were provided", name, bytes_needed, dst.len())));
|
||||||
return Err(Error::from_text(format!("DirectoryRecord for entry {} needs {} bytes but {} bytes were provided", name, bytes_needed, dst.len())));
|
}
|
||||||
}
|
|
||||||
|
unsafe {
|
||||||
unsafe {
|
let dir_record = std::mem::transmute::<&mut u8, &mut DirectoryRecord>(&mut dst[0]);
|
||||||
let dir_record = std::mem::transmute::<&mut u8, &mut DirectoryRecord>(&mut dst[0]);
|
dir_record.new(name, has_system_use);
|
||||||
dir_record.new(name, has_system_use);
|
|
||||||
|
dir_record.data_block_number.write(track_rel_lba);
|
||||||
dir_record.data_block_number.write(track_rel_lba);
|
dir_record.data_size.write(size_bytes);
|
||||||
dir_record.data_size.write(size_bytes);
|
dir_record.time_stamp = SmallDate::now();
|
||||||
dir_record.time_stamp = SmallDate::now();
|
|
||||||
|
if let Some(system_use) = system_use {
|
||||||
if let Some(system_use) = system_use {
|
if let Some(record_system_use) = dir_record.get_cdxa_system_use_mut() {
|
||||||
if let Some(record_system_use) = dir_record.get_cdxa_system_use_mut() {
|
*record_system_use = system_use;
|
||||||
*record_system_use = system_use;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Ok(dir_record)
|
||||||
Ok(dir_record)
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fn write_dir_record(dir_record: &mut [u8], dir_member: &DirectoryRecordMember, has_system_use: bool) -> Result<usize, Error> {
|
||||||
fn write_dir_record(dir_record: &mut [u8], dir_member: &DirectoryRecordMember, has_system_use: bool) -> Result<usize, Error> {
|
match dir_member {
|
||||||
match dir_member {
|
DirectoryRecordMember::Directory{name, track_rel_lba, real_size} => {
|
||||||
DirectoryRecordMember::Directory{name, track_rel_lba, real_size} => {
|
let system_use = {
|
||||||
let system_use = {
|
if has_system_use {
|
||||||
if has_system_use {
|
let mut system_use = CDXASystemUse::default();
|
||||||
let mut system_use = CDXASystemUse::default();
|
|
||||||
|
system_use.file_attribute.set_mode2();
|
||||||
system_use.file_attribute.set_mode2();
|
system_use.file_attribute.set_directory();
|
||||||
system_use.file_attribute.set_directory();
|
|
||||||
|
Some(system_use)
|
||||||
Some(system_use)
|
}
|
||||||
}
|
|
||||||
|
else {
|
||||||
else {
|
None
|
||||||
None
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, round_bytes_mode2_form1(*real_size as usize) as u32, system_use)?;
|
||||||
let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, round_bytes_mode2_form1(*real_size as usize) as u32, system_use)?;
|
|
||||||
|
dir_record.set_directory();
|
||||||
dir_record.set_directory();
|
return Ok(dir_record.length[0] as usize);
|
||||||
return Ok(dir_record.length[0] as usize);
|
},
|
||||||
},
|
|
||||||
|
DirectoryRecordMember::File{name, track_rel_lba, real_size} => {
|
||||||
DirectoryRecordMember::File{name, track_rel_lba, real_size} => {
|
let system_use = {
|
||||||
let system_use = {
|
if has_system_use {
|
||||||
if has_system_use {
|
let mut system_use = CDXASystemUse::default();
|
||||||
let mut system_use = CDXASystemUse::default();
|
|
||||||
|
system_use.file_attribute.set_mode2();
|
||||||
system_use.file_attribute.set_mode2();
|
Some(system_use)
|
||||||
Some(system_use)
|
}
|
||||||
}
|
|
||||||
|
else {
|
||||||
else {
|
None
|
||||||
None
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, *real_size as u32, system_use)?;
|
||||||
let dir_record = create_dir_record_raw(dir_record, name.as_str(), *track_rel_lba as u32, *real_size as u32, system_use)?;
|
|
||||||
|
dir_record.set_file();
|
||||||
dir_record.set_file();
|
return Ok(dir_record.length[0] as usize);
|
||||||
return Ok(dir_record.length[0] as usize);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fn create_wrong_padding_error(for_who: &str, name: String, padded_size: usize, real_size: usize) -> Error {
|
||||||
fn create_wrong_padding_error(for_who: &str, name: String, padded_size: usize, real_size: usize) -> Error {
|
Error::from_text(format!("Encoding-Error for {} {}: Padded size ({}) is smaller then the original size ({}).", for_who, name, padded_size, real_size))
|
||||||
Error::from_text(format!("Encoding-Error for {} {}: Padded size ({}) is smaller then the original size ({}).", for_who, name, padded_size, real_size))
|
|
||||||
}
|
}
|
|
@ -1,66 +1,66 @@
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use psxcdgen_ex::{encoder::{EncodingFunctions, psx::{calculate_psx_lbas, calculate_psx_length_for, encode_psx_image}}, file_writer::{ImageType, write_image}, config_reader};
|
use psxcdgen_ex::{encoder::{EncodingFunctions, psx::{calculate_psx_lbas, calculate_psx_length_for, encode_psx_image}}, file_writer::{ImageType, write_image}, config_reader};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tool_helper::{Error, exit_with_error, read_file_to_string};
|
use tool_helper::{Error, exit_with_error, read_file_to_string};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(about = "Creates an ISO image from a description file", long_about = None)]
|
#[clap(about = "Creates an ISO image from a description file", long_about = None)]
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
#[clap(value_enum, value_parser, help="Specifies the system for which to create the disc image")]
|
#[clap(value_enum, value_parser, help="Specifies the system for which to create the disc image")]
|
||||||
system_type: SystemType,
|
system_type: SystemType,
|
||||||
|
|
||||||
#[clap(value_enum, value_parser, help="Specifies the disc image type")]
|
#[clap(value_enum, value_parser, help="Specifies the disc image type")]
|
||||||
output_type: ImageType,
|
output_type: ImageType,
|
||||||
|
|
||||||
#[clap(short='o', help="Output path; Some OUTPUT_TYPE might create additional files with different endings")]
|
#[clap(short='o', help="Output path; Some OUTPUT_TYPE might create additional files with different endings")]
|
||||||
output_file: PathBuf,
|
output_file: PathBuf,
|
||||||
|
|
||||||
#[clap(long="list", id="Path to file", help="If set will list the CD content to stdout; With path will output list to file")]
|
#[clap(long="list", id="Path to file", help="If set will list the CD content to stdout; With path will output list to file")]
|
||||||
list_content: Option<Option<PathBuf>>,
|
list_content: Option<Option<PathBuf>>,
|
||||||
|
|
||||||
#[clap(value_parser, help="Input XML path for creating the image")]
|
#[clap(value_parser, help="Input XML path for creating the image")]
|
||||||
input_file: PathBuf,
|
input_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||||
enum SystemType {
|
enum SystemType {
|
||||||
Psx
|
Psx
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemType {
|
impl SystemType {
|
||||||
pub fn get_encoding_functions(self) -> EncodingFunctions {
|
pub fn get_encoding_functions(self) -> EncodingFunctions {
|
||||||
match self {
|
match self {
|
||||||
SystemType::Psx => EncodingFunctions{length_calculator: calculate_psx_length_for, lba_calculator: calculate_psx_lbas, encoder: encode_psx_image}
|
SystemType::Psx => EncodingFunctions{length_calculator: calculate_psx_length_for, lba_calculator: calculate_psx_lbas, encoder: encode_psx_image}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(cmd_line: CommandLine) -> Result<(), Error> {
|
fn run_main(cmd_line: CommandLine) -> Result<(), Error> {
|
||||||
let encoding_functions = cmd_line.system_type.get_encoding_functions();
|
let encoding_functions = cmd_line.system_type.get_encoding_functions();
|
||||||
let (desc, lba_embedded_files) = psxcdgen_ex::process(config_reader::parse_xml(read_file_to_string(&cmd_line.input_file)?)?, encoding_functions.lba_calculator)?;
|
let (desc, lba_embedded_files) = psxcdgen_ex::process(config_reader::parse_xml(read_file_to_string(&cmd_line.input_file)?)?, encoding_functions.lba_calculator)?;
|
||||||
let file_map = desc.create_file_map();
|
let file_map = desc.create_file_map();
|
||||||
|
|
||||||
psxcdgen_ex::process_files(file_map, lba_embedded_files, encoding_functions.length_calculator)?;
|
psxcdgen_ex::process_files(file_map, lba_embedded_files, encoding_functions.length_calculator)?;
|
||||||
write_image(&desc, encoding_functions.encoder, cmd_line.output_type, cmd_line.output_file)?;
|
write_image(&desc, encoding_functions.encoder, cmd_line.output_type, cmd_line.output_file)?;
|
||||||
|
|
||||||
if let Some(list_content_option) = cmd_line.list_content {
|
if let Some(list_content_option) = cmd_line.list_content {
|
||||||
psxcdgen_ex::dump_content(&desc, tool_helper::open_output(list_content_option)?)
|
psxcdgen_ex::dump_content(&desc, tool_helper::open_output(&list_content_option)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match CommandLine::try_parse() {
|
match CommandLine::try_parse() {
|
||||||
Ok(cmd_line) => {
|
Ok(cmd_line) => {
|
||||||
if let Err(error) = run_main(cmd_line) {
|
if let Err(error) = run_main(cmd_line) {
|
||||||
exit_with_error(error)
|
exit_with_error(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("{}", error)
|
println!("{}", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,117 +1,117 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use crossterm::{event::{self, Event as CEvent, KeyboardEnhancementFlags, KeyEventKind, PushKeyboardEnhancementFlags}, execute, terminal};
|
use crossterm::{event::{self, Event as CEvent, KeyboardEnhancementFlags, KeyEventKind, PushKeyboardEnhancementFlags}, execute, terminal};
|
||||||
use psxreadmap::{ConsoleUI, Event, EventReceiver, Terminal, load_memory_map, UIState};
|
use psxreadmap::{ConsoleUI, Event, EventReceiver, Terminal, load_memory_map, UIState};
|
||||||
use std::{io::{self, Stdout}, path::PathBuf, sync::mpsc, thread, time::{Duration, Instant}};
|
use std::{io::{self, Stdout}, path::PathBuf, sync::mpsc, thread, time::{Duration, Instant}};
|
||||||
use tool_helper::{Error, exit_with_error, open_output};
|
use tool_helper::{Error, exit_with_error, open_output};
|
||||||
use ratatui::backend::CrosstermBackend;
|
use ratatui::backend::CrosstermBackend;
|
||||||
|
|
||||||
pub const RUN_DUMP_TOOL_IN_WSL:bool = cfg!(windows);
|
pub const RUN_DUMP_TOOL_IN_WSL:bool = cfg!(windows);
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(about = "Opens and scans an ELF file to print extended memory information", long_about = None)]
|
#[clap(about = "Opens and scans an ELF file to print extended memory information", long_about = None)]
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
#[clap(value_parser, help="Input ELF file for scannning")]
|
#[clap(value_parser, help="Input ELF file for scannning")]
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
#[clap(long="wsl", help="Run \"objdump\" in WSL", default_value_t=RUN_DUMP_TOOL_IN_WSL)]
|
#[clap(long="wsl", help="Run \"objdump\" in WSL", default_value_t=RUN_DUMP_TOOL_IN_WSL)]
|
||||||
use_wsl: bool,
|
use_wsl: bool,
|
||||||
#[clap(long="raw", default_value_t=false)]
|
#[clap(long="raw", default_value_t=false)]
|
||||||
raw_dump: bool,
|
raw_dump: bool,
|
||||||
#[clap(short='o', help="Output a memory map file with running the tool")]
|
#[clap(short='o', help="Output a memory map file with running the tool")]
|
||||||
output: Option<PathBuf>
|
output: Option<PathBuf>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
match CommandLine::try_parse() {
|
match CommandLine::try_parse() {
|
||||||
Ok(cmd_line) => {
|
Ok(cmd_line) => {
|
||||||
match run_main(cmd_line) {
|
match run_main(cmd_line) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(error) => exit_with_error(error)
|
Err(error) => exit_with_error(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("{})", error)
|
println!("{})", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
fn run_main(cmd: CommandLine) -> Result<(), Error> {
|
||||||
if cmd.raw_dump {
|
if cmd.raw_dump {
|
||||||
psxreadmap::get_tool_output(cmd.use_wsl, cmd.input, open_output(cmd.output)?)?;
|
psxreadmap::get_tool_output(cmd.use_wsl, cmd.input, open_output(&cmd.output)?)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory_map = load_memory_map(cmd.use_wsl, cmd.input)?; dump_memory_map(cmd.output, &memory_map)?;
|
let memory_map = load_memory_map(cmd.use_wsl, cmd.input)?; dump_memory_map(cmd.output, &memory_map)?;
|
||||||
let rx = start_event_loop();
|
let rx = start_event_loop();
|
||||||
let terminal = setup_console()?;
|
let terminal = setup_console()?;
|
||||||
let mut console_ui = ConsoleUI::new(rx, terminal);
|
let mut console_ui = ConsoleUI::new(rx, terminal);
|
||||||
|
|
||||||
console_ui.update_data(memory_map)?;
|
console_ui.update_data(memory_map)?;
|
||||||
loop {
|
loop {
|
||||||
match console_ui.update()? {
|
match console_ui.update()? {
|
||||||
UIState::Alive => (),
|
UIState::Alive => (),
|
||||||
UIState::Render => {console_ui.render()?;}
|
UIState::Render => {console_ui.render()?;}
|
||||||
UIState::Terminated => break
|
UIState::Terminated => break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console_ui.close()
|
console_ui.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_memory_map(output: Option<PathBuf>, memory_map: &readmap::types::MemoryMap) -> Result<(), Error> {
|
fn dump_memory_map(output: Option<PathBuf>, memory_map: &readmap::types::MemoryMap) -> Result<(), Error> {
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
let output = tool_helper::open_output(Some(output))?;
|
let output = tool_helper::open_output(&Some(output))?;
|
||||||
|
|
||||||
readmap::dump::write(output, &memory_map)?;
|
readmap::dump::write(output, &memory_map)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_event_loop() -> EventReceiver {
|
fn start_event_loop() -> EventReceiver {
|
||||||
// Set up a mpsc (multiproducer, single consumer) channel to communicate between the input handler and the rendering loop.
|
// Set up a mpsc (multiproducer, single consumer) channel to communicate between the input handler and the rendering loop.
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let tick_rate = Duration::from_millis(200);
|
let tick_rate = Duration::from_millis(200);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut last_tick = Instant::now();
|
let mut last_tick = Instant::now();
|
||||||
|
|
||||||
tx.send(Event::ForceRender).expect("Send ForceRender command!");
|
tx.send(Event::ForceRender).expect("Send ForceRender command!");
|
||||||
loop {
|
loop {
|
||||||
let timeout = tick_rate.checked_sub(last_tick.elapsed()).unwrap_or_else(|| Duration::from_secs(0));
|
let timeout = tick_rate.checked_sub(last_tick.elapsed()).unwrap_or_else(|| Duration::from_secs(0));
|
||||||
if event::poll(timeout).expect("Event poll working") {
|
if event::poll(timeout).expect("Event poll working") {
|
||||||
if let CEvent::Key(key) = event::read().expect("Can read key input") {
|
if let CEvent::Key(key) = event::read().expect("Can read key input") {
|
||||||
if key.kind == KeyEventKind::Release {
|
if key.kind == KeyEventKind::Release {
|
||||||
tx.send(Event::Input(key)).expect("Can send KeyInput");
|
tx.send(Event::Input(key)).expect("Can send KeyInput");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if last_tick.elapsed() >= tick_rate {
|
if last_tick.elapsed() >= tick_rate {
|
||||||
if let Ok(_) = tx.send(Event::Tick) {
|
if let Ok(_) = tx.send(Event::Tick) {
|
||||||
last_tick = Instant::now();
|
last_tick = Instant::now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
rx
|
rx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_console() -> Result<Terminal, Error> {
|
fn setup_console() -> Result<Terminal, Error> {
|
||||||
fn open_stdout() -> Result<Stdout, Error> {
|
fn open_stdout() -> Result<Stdout, Error> {
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
if cfg!(unix) {
|
if cfg!(unix) {
|
||||||
execute!(stdout, PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::REPORT_EVENT_TYPES))?;
|
execute!(stdout, PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::REPORT_EVENT_TYPES))?;
|
||||||
}
|
}
|
||||||
Ok(stdout)
|
Ok(stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal::enable_raw_mode()?;
|
terminal::enable_raw_mode()?;
|
||||||
|
|
||||||
// Setup Crossterm for the Terminal
|
// Setup Crossterm for the Terminal
|
||||||
let stdout = open_stdout()?;
|
let stdout = open_stdout()?;
|
||||||
let backend = CrosstermBackend::new(stdout);
|
let backend = CrosstermBackend::new(stdout);
|
||||||
let mut terminal = Terminal::new(backend)?;
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
Ok(terminal)
|
Ok(terminal)
|
||||||
}
|
}
|
|
@ -1,221 +1,225 @@
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use envmnt::{ExpandOptions, ExpansionType};
|
use envmnt::{ExpandOptions, ExpansionType};
|
||||||
use std::{boxed::Box, io::{BufRead, BufReader, BufWriter, Read, Write}, path::PathBuf};
|
use std::{boxed::Box, io::{BufRead, BufReader, BufWriter, Read, Write}, path::PathBuf};
|
||||||
|
|
||||||
pub mod bits;
|
pub mod bits;
|
||||||
pub mod compress;
|
pub mod compress;
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
|
|
||||||
pub type BufferedInputFile = BufReader<std::fs::File>;
|
pub type BufferedInputFile = BufReader<std::fs::File>;
|
||||||
pub type Output = Box<dyn Write>;
|
pub type Output = Box<dyn Write>;
|
||||||
pub type Input = Box<dyn BufRead>;
|
pub type Input = Box<dyn BufRead>;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! format_if_error {
|
macro_rules! format_if_error {
|
||||||
($result:expr, $format_text:literal) => {
|
($result:expr, $format_text:literal) => {
|
||||||
tool_helper::callback_if_any_error($result, |error_text| {
|
tool_helper::callback_if_any_error($result, |error_text| {
|
||||||
format!($format_text, error_text=error_text)
|
format!($format_text, error_text=error_text)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($result:expr, $format_text:literal, $($arg:expr)*) => {
|
($result:expr, $format_text:literal, $($arg:expr)*) => {
|
||||||
tool_helper::callback_if_any_error($result, |error_text| {
|
tool_helper::callback_if_any_error($result, |error_text| {
|
||||||
format!($format_text, $($arg),*, error_text=error_text)
|
format!($format_text, $($arg),*, error_text=error_text)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! format_if_error_drop_cause {
|
macro_rules! format_if_error_drop_cause {
|
||||||
($result:expr, $format_text:literal) => {
|
($result:expr, $format_text:literal) => {
|
||||||
tool_helper::callback_if_any_error($result, |error_text| {
|
tool_helper::callback_if_any_error($result, |error_text| {
|
||||||
format!($format_text)
|
format!($format_text)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
($result:expr, $format_text:literal, $($arg:expr)*) => {
|
($result:expr, $format_text:literal, $($arg:expr)*) => {
|
||||||
tool_helper::callback_if_any_error($result, |error_text| {
|
tool_helper::callback_if_any_error($result, |error_text| {
|
||||||
format!($format_text, $($arg),*)
|
format!($format_text, $($arg),*)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
pub exit_code: i32,
|
pub exit_code: i32,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
const DEFAULT_EXITCODE:i32 = -1;
|
const DEFAULT_EXITCODE:i32 = -1;
|
||||||
|
|
||||||
pub fn from_str(str: &str) -> Error {
|
pub fn from_str(str: &str) -> Error {
|
||||||
Self::from_text(str.to_owned())
|
Self::from_text(str.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_text(text: String) -> Error {
|
pub fn from_text(text: String) -> Error {
|
||||||
Error{exit_code: Self::DEFAULT_EXITCODE, text}
|
Error{exit_code: Self::DEFAULT_EXITCODE, text}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_error<T>(error: T) -> Error where T: std::fmt::Display {
|
pub fn from_error<T>(error: T) -> Error where T: std::fmt::Display {
|
||||||
Error::from_text(error.to_string())
|
Error::from_text(error.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_core_error<T>(error: T) -> Error where T: core::fmt::Display {
|
pub fn from_core_error<T>(error: T) -> Error where T: core::fmt::Display {
|
||||||
Error::from_text(error.to_string())
|
Error::from_text(error.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_callback<F>(callback: F) -> Error where F: Fn() -> String {
|
pub fn from_callback<F>(callback: F) -> Error where F: Fn() -> String {
|
||||||
Error::from_text(callback())
|
Error::from_text(callback())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_implemented(function: &str) -> Error {
|
pub fn not_implemented(function: &str) -> Error {
|
||||||
Error::from_text(format!("{} not implemented yet", function))
|
Error::from_text(format!("{} not implemented yet", function))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_or_new<T, S>(result: std::result::Result<T, S>) -> Result<T, Error> where S: std::fmt::Display {
|
pub fn try_or_new<T, S>(result: std::result::Result<T, S>) -> Result<T, Error> where S: std::fmt::Display {
|
||||||
match result {
|
match result {
|
||||||
Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
Err(error) => Err(Error{exit_code: Self::DEFAULT_EXITCODE, text: error.to_string()}),
|
Err(error) => Err(Error{exit_code: Self::DEFAULT_EXITCODE, text: error.to_string()}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ok_or_new<T, F>(option: Option<T>, error_text: F) -> Result<T, Error> where F: Fn () -> String{
|
pub fn ok_or_new<T, F>(option: Option<T>, error_text: F) -> Result<T, Error> where F: Fn () -> String{
|
||||||
Ok(option.ok_or(Error::from_callback(error_text))?)
|
Ok(option.ok_or(Error::from_callback(error_text))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_generic_to_std_err<T: std::fmt::Display>(object: &T) {
|
pub fn print_generic_to_std_err<T: std::fmt::Display>(object: &T) {
|
||||||
eprintln!("{}", format!("ERROR: {}", object).red());
|
eprintln!("{}", format!("ERROR: {}", object).red());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_to_std_err(&self) {
|
pub fn print_to_std_err(&self) {
|
||||||
Self::print_generic_to_std_err(self)
|
Self::print_generic_to_std_err(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.text)
|
write!(f, "{}", self.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<std::io::Error> for Error {
|
impl std::convert::From<std::io::Error> for Error {
|
||||||
fn from(error: std::io::Error) -> Self {
|
fn from(error: std::io::Error) -> Self {
|
||||||
Error{exit_code: -1, text: error.to_string()}
|
Error{exit_code: -1, text: error.to_string()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<cdtypes::Error> for Error {
|
impl std::convert::From<cdtypes::Error> for Error {
|
||||||
fn from(error: cdtypes::Error) -> Self {
|
fn from(error: cdtypes::Error) -> Self {
|
||||||
Error::from_error(error)
|
Error::from_error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<std::convert::Infallible> for Error {
|
impl std::convert::From<std::convert::Infallible> for Error {
|
||||||
fn from(error: std::convert::Infallible) -> Self {
|
fn from(error: std::convert::Infallible) -> Self {
|
||||||
Error::from_error(error)
|
Error::from_error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<std::sync::mpsc::RecvError> for Error {
|
impl std::convert::From<std::sync::mpsc::RecvError> for Error {
|
||||||
fn from(error: std::sync::mpsc::RecvError) -> Self {
|
fn from(error: std::sync::mpsc::RecvError) -> Self {
|
||||||
Error::from_error(error)
|
Error::from_error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit_with_error(error: Error) {
|
pub fn exit_with_error(error: Error) {
|
||||||
error.print_to_std_err();
|
error.print_to_std_err();
|
||||||
std::process::exit(error.exit_code);
|
std::process::exit(error.exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prefix_if_error<T>(prefix: &str, result: Result<T, Error>) -> Result<T, Error> {
|
pub fn print_warning(str: String) {
|
||||||
match result {
|
eprintln!("{}", str.yellow());
|
||||||
Ok(value) => Ok(value),
|
}
|
||||||
Err(mut error) => {
|
|
||||||
let mut new_text = String::from(prefix);
|
pub fn prefix_if_error<T>(prefix: &str, result: Result<T, Error>) -> Result<T, Error> {
|
||||||
|
match result {
|
||||||
new_text.push_str(error.text.as_str());
|
Ok(value) => Ok(value),
|
||||||
error.text = new_text;
|
Err(mut error) => {
|
||||||
Err(error)
|
let mut new_text = String::from(prefix);
|
||||||
},
|
|
||||||
}
|
new_text.push_str(error.text.as_str());
|
||||||
}
|
error.text = new_text;
|
||||||
|
Err(error)
|
||||||
pub fn callback_if_error<F: Fn(String) -> String, T>(result: Result<T, Error>, callback: F) -> Result<T, Error> {
|
},
|
||||||
match result {
|
}
|
||||||
Ok(value) => Ok(value),
|
}
|
||||||
Err(mut error) => {
|
|
||||||
error.text = callback(error.text);
|
pub fn callback_if_error<F: Fn(String) -> String, T>(result: Result<T, Error>, callback: F) -> Result<T, Error> {
|
||||||
|
match result {
|
||||||
Err(error)
|
Ok(value) => Ok(value),
|
||||||
}
|
Err(mut error) => {
|
||||||
}
|
error.text = callback(error.text);
|
||||||
}
|
|
||||||
|
Err(error)
|
||||||
pub fn callback_if_any_error<F: Fn(String) -> String, T, E: std::string::ToString>(result: Result<T, E>, callback: F) -> Result<T, Error> {
|
}
|
||||||
match result {
|
}
|
||||||
Ok(value) => Ok(value),
|
}
|
||||||
Err(error) => {
|
|
||||||
Err(Error::from_text(callback(error.to_string())))
|
pub fn callback_if_any_error<F: Fn(String) -> String, T, E: std::string::ToString>(result: Result<T, E>, callback: F) -> Result<T, Error> {
|
||||||
}
|
match result {
|
||||||
}
|
Ok(value) => Ok(value),
|
||||||
}
|
Err(error) => {
|
||||||
|
Err(Error::from_text(callback(error.to_string())))
|
||||||
pub fn path_with_env_from(path: &str) -> PathBuf {
|
}
|
||||||
PathBuf::from(envmnt::expand(path, Some(ExpandOptions{expansion_type: Some(ExpansionType::All), default_to_empty: false})))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_output_file(output_path: &PathBuf) -> Result<BufWriter<std::fs::File>, Error> {
|
pub fn path_with_env_from(path: &str) -> PathBuf {
|
||||||
Ok(std::io::BufWriter::new(std::fs::File::create(output_path)?))
|
PathBuf::from(envmnt::expand(path, Some(ExpandOptions{expansion_type: Some(ExpansionType::All), default_to_empty: false})))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_output(output_file: Option<PathBuf>) -> Result<Output, Error> {
|
pub fn open_output_file(output_path: &PathBuf) -> Result<BufWriter<std::fs::File>, Error> {
|
||||||
match output_file {
|
Ok(std::io::BufWriter::new(std::fs::File::create(output_path)?))
|
||||||
Some(output_path) => Ok(Box::new(open_output_file(&output_path)?)),
|
}
|
||||||
None => Ok(Box::new(BufWriter::new(std::io::stdout()))),
|
|
||||||
}
|
pub fn open_output(output_file: &Option<PathBuf>) -> Result<Output, Error> {
|
||||||
}
|
match output_file {
|
||||||
|
Some(output_path) => Ok(Box::new(open_output_file(&output_path)?)),
|
||||||
pub fn open_input(input_file: Option<PathBuf>) -> Result<Input, Error> {
|
None => Ok(Box::new(BufWriter::new(std::io::stdout()))),
|
||||||
match input_file {
|
}
|
||||||
Some(input_path) => Ok(Box::new(open_input_file_buffered(&input_path)?)),
|
}
|
||||||
None => Ok(Box::new(BufReader::new(std::io::stdin()))),
|
|
||||||
}
|
pub fn open_input(input_file: Option<PathBuf>) -> Result<Input, Error> {
|
||||||
}
|
match input_file {
|
||||||
|
Some(input_path) => Ok(Box::new(open_input_file_buffered(&input_path)?)),
|
||||||
pub fn open_input_file_buffered(input_path: &PathBuf) -> Result<BufferedInputFile, Error> {
|
None => Ok(Box::new(BufReader::new(std::io::stdin()))),
|
||||||
Ok(BufReader::new(std::fs::File::open(input_path)?))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn os_str_to_string(input: &std::ffi::OsStr, name: &str) -> Result<String, Error> {
|
pub fn open_input_file_buffered(input_path: &PathBuf) -> Result<BufferedInputFile, Error> {
|
||||||
Ok(Error::ok_or_new(input.to_str(), ||format!("Converting {} to UTF-8 failed", name))?.to_owned())
|
Ok(BufReader::new(std::fs::File::open(input_path)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file_name_from_path_buf(input: &PathBuf, name: &str) -> Result<String, Error> {
|
pub fn os_str_to_string(input: &std::ffi::OsStr, name: &str) -> Result<String, Error> {
|
||||||
os_str_to_string(Error::ok_or_new(input.file_name(), ||format!("No {} file name found", name))?, name)
|
Ok(Error::ok_or_new(input.to_str(), ||format!("Converting {} to UTF-8 failed", name))?.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_to_vec(input: Input) -> Result<Vec<u8>, Error> {
|
pub fn get_file_name_from_path_buf(input: &PathBuf, name: &str) -> Result<String, Error> {
|
||||||
let mut data = Vec::new();
|
os_str_to_string(Error::ok_or_new(input.file_name(), ||format!("No {} file name found", name))?, name)
|
||||||
|
}
|
||||||
for byte in input.bytes() {
|
|
||||||
data.push(byte?);
|
pub fn input_to_vec(input: Input) -> Result<Vec<u8>, Error> {
|
||||||
}
|
let mut data = Vec::new();
|
||||||
|
|
||||||
Ok(data)
|
for byte in input.bytes() {
|
||||||
}
|
data.push(byte?);
|
||||||
|
}
|
||||||
pub fn read_file(file_path: &PathBuf) -> Result<Vec<u8>, Error> {
|
|
||||||
match std::fs::read(file_path) {
|
Ok(data)
|
||||||
Ok(data) => Ok(data),
|
}
|
||||||
Err(error) => create_file_read_error(file_path, error),
|
|
||||||
}
|
pub fn read_file(file_path: &PathBuf) -> Result<Vec<u8>, Error> {
|
||||||
}
|
match std::fs::read(file_path) {
|
||||||
|
Ok(data) => Ok(data),
|
||||||
pub fn read_file_to_string(file_path: &PathBuf) -> Result<String, Error> {
|
Err(error) => create_file_read_error(file_path, error),
|
||||||
match std::fs::read_to_string(file_path) {
|
}
|
||||||
Ok(string) => Ok(string),
|
}
|
||||||
Err(error) => create_file_read_error(file_path, error),
|
|
||||||
}
|
pub fn read_file_to_string(file_path: &PathBuf) -> Result<String, Error> {
|
||||||
}
|
match std::fs::read_to_string(file_path) {
|
||||||
|
Ok(string) => Ok(string),
|
||||||
fn create_file_read_error<T>(file_path: &PathBuf, error: std::io::Error) -> Result<T, Error> {
|
Err(error) => create_file_read_error(file_path, error),
|
||||||
Err(Error::from_text(format!("Failed reading file {} with error: \"{}\"", file_path.display(), error)))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_file_read_error<T>(file_path: &PathBuf, error: std::io::Error) -> Result<T, Error> {
|
||||||
|
Err(Error::from_text(format!("Failed reading file {} with error: \"{}\"", file_path.display(), error)))
|
||||||
}
|
}
|
Loading…
Reference in New Issue