Moved psxcdgen files to tools folder
This commit is contained in:
9
src/Tools/psxcdread/Cargo.toml
Normal file
9
src/Tools/psxcdread/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "psxcdread"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cdtypes = {path = "../cdtypes"}
|
137
src/Tools/psxcdread/src/complex_dump/mod.rs
Normal file
137
src/Tools/psxcdread/src/complex_dump/mod.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use cdtypes::{Error, cd::{sector::Sector, CD}};
|
||||
|
||||
pub fn check_edc(cd: &CD, sector_id: usize) -> Result<(), Error> {
|
||||
if let Some(sector) = cd.get_sector(sector_id) {
|
||||
if let Some((edc, _)) = sector.get_error_correction() {
|
||||
let mut sector = (*sector).clone();
|
||||
|
||||
sector.calculate_edc();
|
||||
if let Some(calc_edc) = sector.get_edc() {
|
||||
println!("ISO EDC is: {}", edc);
|
||||
println!("Calculated EDC is: {}", calc_edc);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
println!("No EDC");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError(format!("Sector {} not found", sector_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_ecc(cd: &CD, sector_id: usize) -> Result<(), Error> {
|
||||
if let Some(sector) = cd.get_sector(sector_id) {
|
||||
if let Some((_, Some(ecc))) = sector.get_error_correction() {
|
||||
let mut sector = (*sector).clone();
|
||||
|
||||
sector.calculate_ecc();
|
||||
if let Some(calc_ecc) = sector.get_ecc() {
|
||||
if ecc == calc_ecc {
|
||||
println!("ECC is same");
|
||||
}
|
||||
|
||||
else {
|
||||
println!("ECC is different");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
println!("No ECC");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError(format!("Sector {} not found", sector_id)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump(cd: &CD) -> Result<(), Error> {
|
||||
println!("{}", cd.get_primary_volume_descriptor()?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_root_dir_rec(cd: &CD, detailed: bool) -> Result<(), Error> {
|
||||
let root_dir = cd.get_primary_volume_descriptor()?.get_root_dir_record();
|
||||
|
||||
dump_dir_rec(cd, root_dir.data_block_number.read_any() as usize, detailed)
|
||||
}
|
||||
|
||||
pub fn dump_dir_rec(cd: &CD, sector_id: usize, detailed: bool) -> Result<(), Error> {
|
||||
for entry in cd.directory_record_iter(sector_id) {
|
||||
if detailed {
|
||||
println!("{}", entry)
|
||||
}
|
||||
|
||||
else {
|
||||
let type_name = {
|
||||
if entry.is_file() {
|
||||
"File"
|
||||
}
|
||||
|
||||
else {
|
||||
"Directory"
|
||||
}
|
||||
};
|
||||
println!("{}: {} @{}", type_name, entry.get_name(), entry.data_block_number.read_any());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_path_table(cd: &CD) -> Result<(), Error> {
|
||||
let mut number = 1usize;
|
||||
|
||||
for entry in cd.path_table_iter() {
|
||||
println!("{}.) {}", number, entry);
|
||||
number += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_stats(cd: &CD) -> Result<(), Error> {
|
||||
let mut audio_count = 0;
|
||||
let mut mode0_count = 0;
|
||||
let mut mode1_count = 0;
|
||||
let mut mode2form1_count = 0;
|
||||
let mut mode2form2_count = 0;
|
||||
|
||||
for sector in cd.sector_iter() {
|
||||
match sector {
|
||||
Sector::Audio(_) => audio_count += 1,
|
||||
Sector::Empty(_) => mode0_count += 1,
|
||||
Sector::CDData(_) => mode1_count += 1,
|
||||
Sector::CDXAData(_) => mode2form1_count += 1,
|
||||
Sector::CDXAAudio(_) => mode2form2_count += 1,
|
||||
}
|
||||
}
|
||||
|
||||
println!("Audio: {}\nMode0: {}\nMode1: {}\nMode2Form1: {}\nMode2Form2: {}", audio_count, mode0_count, mode1_count, mode2form1_count, mode2form2_count);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn dump_tracks(cd: &CD) -> Result<(), Error> {
|
||||
if cd.has_tracks() {
|
||||
let mut track_count = 1;
|
||||
|
||||
for track_info in cd.track_info_iter() {
|
||||
println!("Track {:02}: {:<10} @{}", track_count, track_info.r#type, track_info.start);
|
||||
track_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
println!("Track 01");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
1
src/Tools/psxcdread/src/lib.rs
Normal file
1
src/Tools/psxcdread/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod complex_dump;
|
379
src/Tools/psxcdread/src/main.rs
Normal file
379
src/Tools/psxcdread/src/main.rs
Normal file
@@ -0,0 +1,379 @@
|
||||
use psxcdread::{*};
|
||||
use cdtypes::{Error, cd::CD, types::{cue, sector::SECTOR_SIZE}};
|
||||
use std::{fs::OpenOptions, io::{stdin, stdout, Write}, path::{Path, PathBuf}};
|
||||
|
||||
fn open_file(path: &Path, write_access: bool) -> Result<std::fs::File, Error> {
|
||||
match OpenOptions::new().read(true).write(write_access).create(write_access).open(path) {
|
||||
Ok(file) => Ok(file),
|
||||
Err(error) => {
|
||||
let path = {
|
||||
if let Some(path) = path.to_str() {
|
||||
path.to_owned()
|
||||
}
|
||||
|
||||
else {
|
||||
"<Invalid encoded file path>".to_owned()
|
||||
}
|
||||
};
|
||||
Err(Error::IOError(Some(path), error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_cue(file: std::fs::File, cue_path: &Path) -> Result<CD, Error> {
|
||||
fn get_bin_path(specifier: &Vec<cue::Specifier>, cue_path: &Path) -> Result<Option<PathBuf>, Error> {
|
||||
for specifier in specifier {
|
||||
match specifier {
|
||||
cue::Specifier::File{path, format} => {
|
||||
if matches!(format, cue::Format::Binary) {
|
||||
let mut bin_path = cue_path.to_path_buf();
|
||||
|
||||
if let Some(_) = bin_path.file_name() {
|
||||
bin_path.pop();
|
||||
}
|
||||
|
||||
bin_path.push(path);
|
||||
return Ok(Some(bin_path));
|
||||
}
|
||||
|
||||
else {
|
||||
return Err(Error::GenericError("Only binary CUE files are supported".to_owned()));
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
let specifier = cue::read::read(file)?;
|
||||
if let Some(bin_path) = get_bin_path(&specifier, cue_path)? {
|
||||
Ok(CD::from_file(open_file(bin_path.as_path(), false)?, Some(specifier))?)
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError("No binary file specified".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
fn open_cd(path: &str) -> Result<CD, Error> {
|
||||
fn is_cue(path: &Path) -> bool {
|
||||
if let Some(extension) = path.extension() {
|
||||
extension == "cue"
|
||||
}
|
||||
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
let path = Path::new(path);
|
||||
let file = open_file(path, false)?;
|
||||
|
||||
if is_cue(&path) {
|
||||
open_cue(file, &path)
|
||||
}
|
||||
|
||||
else {
|
||||
Ok(CD::from_file(file, None)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_input(cd: &CD, cur_sec: usize) -> Result<String, std::io::Error> {
|
||||
println!("Current Sector: [{}/{}]", cur_sec, cd.last_sector_idx());
|
||||
stdout().write("> ".as_bytes())?;
|
||||
stdout().flush()?;
|
||||
|
||||
let mut input = String::new();
|
||||
stdin().read_line(&mut input)?;
|
||||
Ok(input)
|
||||
}
|
||||
|
||||
enum ChangeSectorNumMode {
|
||||
Increment,
|
||||
Decrement,
|
||||
Absolute
|
||||
}
|
||||
|
||||
fn get_number_from_str(number_str: &str) -> Option<usize> {
|
||||
let (number, base) = {
|
||||
if number_str.starts_with("0x") {
|
||||
(number_str.trim_start_matches("0x"), 16)
|
||||
}
|
||||
|
||||
else {
|
||||
(number_str, 10)
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(number) = usize::from_str_radix(number, base) {
|
||||
Some(number)
|
||||
}
|
||||
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn change_sector_number(mode: ChangeSectorNumMode, cd: &CD, mut cur_sec: usize, mut iter: std::str::Split<&str>) -> usize {
|
||||
if let Some(number) = iter.next() {
|
||||
let last_sec = cd.last_sector_idx();
|
||||
|
||||
if let Some(number) = get_number_from_str(number) {
|
||||
let new_sec = {
|
||||
match mode {
|
||||
ChangeSectorNumMode::Increment => cur_sec + number,
|
||||
ChangeSectorNumMode::Decrement => cur_sec - number,
|
||||
ChangeSectorNumMode::Absolute => number
|
||||
}
|
||||
};
|
||||
|
||||
if new_sec <= last_sec {
|
||||
cur_sec = new_sec;
|
||||
}
|
||||
|
||||
else {
|
||||
println!("{} is higher then last valid index which is {}", new_sec, last_sec);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
println!("{} is no valid number", number);
|
||||
}
|
||||
}
|
||||
|
||||
cur_sec
|
||||
}
|
||||
|
||||
fn write_data(cd: &CD, cur_sec: usize, mut iter: std::str::Split<&str>) -> Result<(), Error> {
|
||||
if let Some(number_str) = iter.next() {
|
||||
let file_name = {
|
||||
let mut file_name = String::new();
|
||||
for str_part in iter {
|
||||
file_name.push_str(str_part);
|
||||
file_name.push(' ');
|
||||
}
|
||||
file_name
|
||||
};
|
||||
|
||||
if !file_name.is_empty() {
|
||||
if let Some(number) = get_number_from_str(number_str) {
|
||||
let mut file = open_file(Path::new(file_name.as_str()), true)?;
|
||||
for i in 0..number {
|
||||
if let Some(sector) = cd.get_sector(cur_sec + i) {
|
||||
file.write(sector.get_data())?;
|
||||
}
|
||||
|
||||
else {
|
||||
return Err(Error::GenericError(format!("Failed writing sector {} - data incomplete", (cur_sec + i))));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError(format!("{} is not a valid number", number_str)))
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError(format!("No output file specified")))
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError(format!("Sector count not specified")))
|
||||
}
|
||||
}
|
||||
|
||||
fn dump(cd: &CD, cur_sec: usize, dump_ecc: bool) {
|
||||
if let Some(sector) = cd.get_sector(cur_sec) {
|
||||
let header: &dyn std::fmt::Display = {
|
||||
if let Some(header) = sector.get_header() {
|
||||
header
|
||||
}
|
||||
|
||||
else {
|
||||
&"<No Header>"
|
||||
}
|
||||
};
|
||||
let sub_header = sector.get_sub_header();
|
||||
|
||||
println!("{}:", sector.get_friendly_name());
|
||||
println!("Header: {}", header);
|
||||
match sub_header {
|
||||
Some(sub_header) => println!("SubHeader: {}", sub_header),
|
||||
None => println!("SubHeader: <None>")
|
||||
}
|
||||
|
||||
let (data_offset, data_size) = sector.get_data_offset_with_size();
|
||||
let data_start = (cur_sec*SECTOR_SIZE) + data_offset;
|
||||
|
||||
println!("Data: at 0x{:X} - 0x{:x} ({} Bytes)", data_start, (data_start + data_size - 1), data_size);
|
||||
let (edc, ecc) = {
|
||||
if let Some(error_cor) = sector.get_error_correction() {
|
||||
(Some(error_cor.0), error_cor.1)
|
||||
}
|
||||
|
||||
else {
|
||||
(None, None)
|
||||
}
|
||||
};
|
||||
println!("EDC: {}\nECC:\n{}", {
|
||||
if let Some(edc) = &edc {
|
||||
edc
|
||||
}
|
||||
|
||||
else {
|
||||
&"<none>" as &dyn std::fmt::Display
|
||||
}
|
||||
}, {
|
||||
if dump_ecc {
|
||||
if let Some(ecc) = &ecc {
|
||||
ecc
|
||||
}
|
||||
|
||||
else {
|
||||
&"<none>" as &dyn std::fmt::Display
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
&"<run \"d ecc\">" as &dyn std::fmt::Display
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_complex(cd: &CD, cur_sec: usize, mut iter: std::str::Split<&str>) -> Result<(), Error> {
|
||||
let handle_dir_parms = |iter| {
|
||||
let mut dump_root = false;
|
||||
let mut dump_detail = false;
|
||||
|
||||
for cmd in iter {
|
||||
match cmd {
|
||||
"root" => dump_root = true,
|
||||
"-d" => dump_detail = true,
|
||||
_ => return Err(Error::GenericError(format!("Unkown option {}", cmd)))
|
||||
}
|
||||
}
|
||||
|
||||
Ok((dump_root, dump_detail))
|
||||
};
|
||||
|
||||
if let Some(cmd) = iter.next() {
|
||||
match cmd {
|
||||
"pvd" => complex_dump::dump(&cd),
|
||||
"dir" => {
|
||||
let (dump_root, dump_detail) = handle_dir_parms(iter)?;
|
||||
|
||||
if dump_root {
|
||||
complex_dump::dump_root_dir_rec(&cd, dump_detail)
|
||||
}
|
||||
|
||||
else {
|
||||
complex_dump::dump_dir_rec(&cd, cur_sec, dump_detail)
|
||||
}
|
||||
},
|
||||
"path" => {
|
||||
complex_dump::dump_path_table(&cd)
|
||||
},
|
||||
"edc" => complex_dump::check_edc(&cd, cur_sec),
|
||||
"ecc" => complex_dump::check_ecc(&cd, cur_sec),
|
||||
"stats" => complex_dump::dump_stats(&cd),
|
||||
"tracks" => complex_dump::dump_tracks(&cd),
|
||||
"?" => {
|
||||
println!("pvd Dump Primary Volume Descriptor");
|
||||
println!("dir [root] [-d] Dumps current sector as Directory Record or the Root Directory Record");
|
||||
println!("path Dumps any PathTable");
|
||||
println!("edc ");
|
||||
println!("ecc ");
|
||||
println!("stats Lists statistics about all sectors");
|
||||
println!("tracks Lists all the tracks of this CD");
|
||||
return Ok(());
|
||||
},
|
||||
_ => {
|
||||
println!("Unkown command {} - try cplx ?", cmd);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::GenericError("No command found".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file = {
|
||||
match std::env::args().skip(1).next()
|
||||
{
|
||||
Some(file) => file,
|
||||
None => {
|
||||
println!("psxcdread <path fo file>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut sec_idx = 0;
|
||||
let cd = {
|
||||
match open_cd(file.as_str()) {
|
||||
Ok(cd) => cd,
|
||||
Err(error) => {
|
||||
println!("Readin CD failed with: {}", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
match read_input(&cd, sec_idx) {
|
||||
Ok(input) => {
|
||||
let mut iter = input.trim().split(" ");
|
||||
if let Some(cmd) = iter.next() {
|
||||
match cmd {
|
||||
"" => (),
|
||||
">" => sec_idx = change_sector_number(ChangeSectorNumMode::Increment, &cd, sec_idx, iter),
|
||||
"<" => sec_idx = change_sector_number(ChangeSectorNumMode::Decrement, &cd, sec_idx, iter),
|
||||
"!" => sec_idx = change_sector_number(ChangeSectorNumMode::Absolute, &cd, sec_idx, iter),
|
||||
"d" => dump(&cd, sec_idx, {
|
||||
if let Some(cmd) = iter.next() {
|
||||
cmd == "ecc"
|
||||
}
|
||||
|
||||
else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
"w" => {
|
||||
if let Err(txt) = write_data(&cd, sec_idx, iter) {
|
||||
println!("{}", txt);
|
||||
}
|
||||
},
|
||||
"cplx" => {
|
||||
if let Err(txt) = handle_complex(&cd, sec_idx, iter) {
|
||||
println!("{}", txt);
|
||||
}
|
||||
}
|
||||
"q" => return,
|
||||
"?" => {
|
||||
println!("> <number> move number sectors forward");
|
||||
println!("< <number> move number sectors backward");
|
||||
println!("! <number> move to sector number");
|
||||
println!("d dump current sector");
|
||||
println!("cplx <sub command> dumps extended infos");
|
||||
println!("w <sector count> <file path> writes the data of the sectos to a file");
|
||||
println!("q exit programm");
|
||||
},
|
||||
_ => println!("Unkown CMD: \"{}\"", cmd)
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
println!("Reading input failed with: {}", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user