diff --git a/cargo-psp/src/bin/mksfo.rs b/cargo-psp/src/bin/mksfo.rs index 28502302..aec2b1a1 100644 --- a/cargo-psp/src/bin/mksfo.rs +++ b/cargo-psp/src/bin/mksfo.rs @@ -1,11 +1,12 @@ use clap::{App, Arg}; use std::collections::HashMap; -use std::path::Path; use std::fs::File; use std::io::prelude::*; use std::io::SeekFrom; +use std::path::Path; -#[repr(C,packed)] +#[repr(C, packed)] +#[derive(Clone, Copy)] struct SfoHeader { magic: u32, version: u32, @@ -15,8 +16,8 @@ struct SfoHeader { } impl SfoHeader { - fn to_le_bytes(self) -> [u8;20] { - let mut buf = [0u8;20]; + fn to_le_bytes(self) -> [u8; 20] { + let mut buf = [0u8; 20]; buf[0..=3].copy_from_slice(&self.magic.to_le_bytes()); buf[4..=7].copy_from_slice(&self.version.to_le_bytes()); @@ -28,7 +29,7 @@ impl SfoHeader { } } -#[repr(C,packed)] +#[repr(C, packed)] #[derive(Default, Debug, Copy, Clone)] struct SfoEntry { key_offset: u16, @@ -40,8 +41,8 @@ struct SfoEntry { } impl SfoEntry { - fn to_le_bytes(self) -> [u8;16] { - let mut buf = [0u8;16]; + fn to_le_bytes(self) -> [u8; 16] { + let mut buf = [0u8; 16]; buf[0..=1].copy_from_slice(&self.key_offset.to_le_bytes()); buf[2..=2].copy_from_slice(&self.alignment.to_le_bytes()); @@ -72,35 +73,40 @@ fn main() { .version("0.1") .author("Paul Sajna ") .about("Creates SFO files used for building Sony PSP EBOOT executables") - .arg(Arg::with_name("bare") - .long("bare") - .help("Do not set any default values. Ignores the value if set.") + .arg( + Arg::with_name("bare") + .long("bare") + .help("Do not set any default values. Ignores the <title> value if set."), ) - .arg(Arg::with_name("dword") - .short("d") - .long("dword") - .help("key=VALUE Add a new DWORD value") - .multiple(true) - .number_of_values(1) - .takes_value(true) + .arg( + Arg::with_name("dword") + .short("d") + .long("dword") + .help("key=VALUE Add a new DWORD value") + .multiple(true) + .number_of_values(1) + .takes_value(true), ) - .arg(Arg::with_name("string") - .short("s") - .long("string") - .help("key=STRING Add a new string value") - .multiple(true) - .number_of_values(1) - .takes_value(true) + .arg( + Arg::with_name("string") + .short("s") + .long("string") + .help("key=STRING Add a new string value") + .multiple(true) + .number_of_values(1) + .takes_value(true), ) - .arg(Arg::with_name("title") - .takes_value(true) - .required(true) - .help("Display title") + .arg( + Arg::with_name("title") + .takes_value(true) + .required(true) + .help("Display title"), ) - .arg(Arg::with_name("output") - .takes_value(true) - .required(true) - .help("Output file name") + .arg( + Arg::with_name("output") + .takes_value(true) + .required(true) + .help("Output file name"), ) .get_matches(); @@ -133,17 +139,44 @@ fn main() { ("CATEGORY", (EntryType::String_, false, true, true, true)), ("DISC_ID", (EntryType::String_, false, false, true, true)), ("DISC_NUMBER", (EntryType::Dword, false, false, false, true)), - ("DISC_VERSION", (EntryType::String_, false, false, true, true)), - ("DRIVER_PATH", (EntryType::String_, false, false, true, false)), + ( + "DISC_VERSION", + (EntryType::String_, false, false, true, true), + ), + ( + "DRIVER_PATH", + (EntryType::String_, false, false, true, false), + ), ("LANGUAGE", (EntryType::String_, false, false, true, false)), - ("PARENTAL_LEVEL", (EntryType::Dword, false, true, true, true)), - ("PSP_SYSTEM_VER", (EntryType::String_, false, false, true, true)), + ( + "PARENTAL_LEVEL", + (EntryType::Dword, false, true, true, true), + ), + ( + "PSP_SYSTEM_VER", + (EntryType::String_, false, false, true, true), + ), ("REGION", (EntryType::Dword, false, false, true, true)), - ("SAVEDATA_DETAIL", (EntryType::String_, false, true, false, false)), - ("SAVEDATA_DIRECTORY", (EntryType::String_, false, true, false, false)), - ("SAVEDATA_FILE_LIST", (EntryType::Binary, false, true, false, false)), - ("SAVEDATA_PARAMS", (EntryType::Binary, false, true, false, false)), - ("SAVEDATA_TITLE", (EntryType::String_, false, true, false, false)), + ( + "SAVEDATA_DETAIL", + (EntryType::String_, false, true, false, false), + ), + ( + "SAVEDATA_DIRECTORY", + (EntryType::String_, false, true, false, false), + ), + ( + "SAVEDATA_FILE_LIST", + (EntryType::Binary, false, true, false, false), + ), + ( + "SAVEDATA_PARAMS", + (EntryType::Binary, false, true, false, false), + ), + ( + "SAVEDATA_TITLE", + (EntryType::String_, false, true, false, false), + ), ("TITLE", (EntryType::String_, false, true, true, true)), ("TITLE_0", (EntryType::String_, false, true, true, true)), ("TITLE_2", (EntryType::String_, false, true, true, true)), @@ -153,24 +186,28 @@ fn main() { ("TITLE_6", (EntryType::String_, false, true, true, true)), ("TITLE_7", (EntryType::String_, false, true, true, true)), ("TITLE_8", (EntryType::String_, false, true, true, true)), - ("UPDATER_VER", (EntryType::String_, false, false, true, false)), - ].iter().cloned().collect(); + ( + "UPDATER_VER", + (EntryType::String_, false, false, true, false), + ), + ] + .iter() + .cloned() + .collect(); if matches.values_of("string").is_some() { for s in matches.values_of("string").unwrap() { - let key_value_pair: Vec<String> = - s.split("=").map(|s: &str| s.to_string()).collect(); + let key_value_pair: Vec<String> = s.split('=').map(|s: &str| s.to_string()).collect(); strings.insert(key_value_pair[0].clone(), key_value_pair[1].clone()); } } if matches.values_of("dword").is_some() { for s in matches.values_of("dword").unwrap() { - let key_value_pair: Vec<String> = - s.split("=").map(|s: &str| s.to_string()).collect(); + let key_value_pair: Vec<String> = s.split('=').map(|s: &str| s.to_string()).collect(); dwords.insert( key_value_pair[0].clone(), - str::parse::<u32>(&key_value_pair[1]).unwrap() + str::parse::<u32>(&key_value_pair[1]).unwrap(), ); } } @@ -179,7 +216,7 @@ fn main() { // TODO reduce copypasta - for (key, _value) in &strings { + for key in strings.keys() { if !valid.contains_key(key.as_str()) { panic!("Invalid option {}", key); } @@ -188,20 +225,20 @@ fn main() { panic!("Key {} does not take a string value", key) } if category == "WG" && !wg { - panic!("Key {} is not valid for category WG", key); + panic!("Key {} is not valid for category WG", key); } if category == "MS" && !ms { - panic!("Key {} is not valid for category MS", key); + panic!("Key {} is not valid for category MS", key); } if category == "MG" && !mg { - panic!("Key {} is not valid for category MG", key); + panic!("Key {} is not valid for category MG", key); } if category == "UG" && !ug { - panic!("Key {} is not valid for category UG", key); + panic!("Key {} is not valid for category UG", key); } } - for (key, _value) in &dwords { + for key in dwords.keys() { if !valid.contains_key(key.as_str()) { panic!("Invalid option {}", key); } @@ -210,16 +247,16 @@ fn main() { panic!("Key {} does not take a dword value", key) } if category == "WG" && !wg { - panic!("Key {} is not valid for category WG", key); + panic!("Key {} is not valid for category WG", key); } if category == "MS" && !ms { - panic!("Key {} is not valid for category MS", key); + panic!("Key {} is not valid for category MS", key); } if category == "MG" && !mg { - panic!("Key {} is not valid for category MG", key); + panic!("Key {} is not valid for category MG", key); } if category == "UG" && !ug { - panic!("Key {} is not valid for category UG", key); + panic!("Key {} is not valid for category UG", key); } } @@ -235,7 +272,10 @@ fn main() { let num_options = dwords.len() + strings.len(); if num_options > MAX_OPTIONS { - panic!("Maximum number of options is {}, you have {}", MAX_OPTIONS, num_options); + panic!( + "Maximum number of options is {}, you have {}", + MAX_OPTIONS, num_options + ); } let mut keys = [0u8; 8192]; @@ -248,10 +288,10 @@ fn main() { let mut sorted_keys: Vec<String> = Vec::new(); for (key, _value) in dwords.iter() { - sorted_keys.push(key.to_string()); + sorted_keys.push(key.to_string()); } for (key, _value) in strings.iter() { - sorted_keys.push(key.to_string()); + sorted_keys.push(key.to_string()); } sorted_keys.sort(); @@ -267,17 +307,15 @@ fn main() { ..Default::default() }; let idx = key_offset as usize; - &keys[idx..idx+key.len()].copy_from_slice(key.as_bytes()); + keys[idx..idx + key.len()].copy_from_slice(key.as_bytes()); key_offset += key.len() as u16 + 1; sfo_entry.val_size = 4; sfo_entry.total_size = 4; let idx = data_offset as usize; - data[idx..idx+4].copy_from_slice(&value.to_le_bytes()); + data[idx..idx + 4].copy_from_slice(&value.to_le_bytes()); data_offset += 4; sfo_entries.push(sfo_entry); - } - - else if strings.contains_key(&key) { + } else if strings.contains_key(&key) { let value = strings.get(&key).unwrap(); header.count += 1; let mut sfo_entry = SfoEntry { @@ -288,7 +326,7 @@ fn main() { ..Default::default() }; let idx = key_offset as usize; - &keys[idx..idx+key.len()].copy_from_slice(key.as_bytes()); + keys[idx..idx + key.len()].copy_from_slice(key.as_bytes()); key_offset += key.len() as u16 + 1; let val_size = value.len() + 1; @@ -296,19 +334,14 @@ fn main() { sfo_entry.val_size = val_size as u32; sfo_entry.total_size = total_size as u32; let idx = data_offset as usize; - data[idx..idx + value.len()].copy_from_slice( - value.as_bytes() - ); + data[idx..idx + value.len()].copy_from_slice(value.as_bytes()); data_offset += total_size as u32; sfo_entries.push(sfo_entry); } } - header.key_offset = ( - core::mem::size_of::<SfoHeader>() + - sfo_entries.len() * - core::mem::size_of::<SfoEntry>() - ) as u32; + header.key_offset = (core::mem::size_of::<SfoHeader>() + + sfo_entries.len() * core::mem::size_of::<SfoEntry>()) as u32; let aligned_val_offset = (header.key_offset + key_offset as u32 + 3) & !3; header.val_offset = aligned_val_offset; @@ -319,6 +352,7 @@ fn main() { file.write_all(&sfo_entry.to_le_bytes()).unwrap(); } file.write_all(&keys[0..key_offset as usize]).unwrap(); - file.seek(SeekFrom::Start(aligned_val_offset as u64)).unwrap(); - file.write(&data[0..data_offset as usize]).unwrap(); + file.seek(SeekFrom::Start(aligned_val_offset as u64)) + .unwrap(); + file.write_all(&data[0..data_offset as usize]).unwrap(); } diff --git a/cargo-psp/src/bin/pack-pbp.rs b/cargo-psp/src/bin/pack-pbp.rs index 9dc55573..525b5d9c 100644 --- a/cargo-psp/src/bin/pack-pbp.rs +++ b/cargo-psp/src/bin/pack-pbp.rs @@ -1,9 +1,10 @@ -use clap::{App, Arg, AppSettings}; +use clap::{App, AppSettings, Arg}; use std::{fs, mem}; const SIGNATURE: [u8; 4] = *b"\0PBP"; const VERSION: u32 = 0x1_0000; +#[derive(Clone, Copy)] struct PbpHeader { signature: [u8; 4], version: u32, @@ -36,55 +37,55 @@ fn main() { Arg::with_name("output.pbp") .takes_value(true) .help("Output PBP file") - .required(true) + .required(true), ) .arg( Arg::with_name("param.sfo") .takes_value(true) .help("Input PARAM.SFO file created with mksfo") - .required(true) + .required(true), ) .arg( Arg::with_name("icon0.png") .takes_value(true) .help("Optional XMB icon") - .required(true) + .required(true), ) .arg( Arg::with_name("icon1.pmf") .takes_value(true) .help("Optional animated XMB icon") - .required(true) + .required(true), ) .arg( Arg::with_name("pic0.png") .takes_value(true) .help("Optional XMB background (overlayed on top of PIC1.PNG)") - .required(true) + .required(true), ) .arg( Arg::with_name("pic1.png") .takes_value(true) .help("Optional XMB background") - .required(true) + .required(true), ) .arg( Arg::with_name("snd0.at3") .takes_value(true) .help("Optional XMB music (when present, sound from ICON1.PMF is ignored)") - .required(true) + .required(true), ) .arg( Arg::with_name("data.psp") .takes_value(true) .help("Executable file") - .required(true) + .required(true), ) .arg( Arg::with_name("data.psar") .takes_value(true) .help("Optional archive data") - .required(true) + .required(true), ) .get_matches(); diff --git a/cargo-psp/src/bin/prxgen.rs b/cargo-psp/src/bin/prxgen.rs index b9847904..df42f648 100644 --- a/cargo-psp/src/bin/prxgen.rs +++ b/cargo-psp/src/bin/prxgen.rs @@ -79,7 +79,7 @@ impl<'a> PrxGen<'a> { let start_idx = sh.sh_offset as usize; let end_idx = sh.sh_size as usize + start_idx; - let relocs = (&bytes[start_idx..end_idx]) + let relocs = bytes[start_idx..end_idx] .chunks(8) .map(|rel_bytes| { Rel::try_from_ctx(rel_bytes, Endian::Little).unwrap().0 diff --git a/cargo-psp/src/bin/prxmin.rs b/cargo-psp/src/bin/prxmin.rs index 102e899d..f15f2b12 100644 --- a/cargo-psp/src/bin/prxmin.rs +++ b/cargo-psp/src/bin/prxmin.rs @@ -209,7 +209,7 @@ fn main() { e_shoff: (DATA_OFFSET + body.len()) as u64, e_phnum: 1, e_shnum: new_sections.len() as u16, - ..elf.header.clone() + ..elf.header }; let ctx = Ctx { diff --git a/cargo-psp/src/fix_imports.rs b/cargo-psp/src/fix_imports.rs index 0473ffa5..9676bc6d 100644 --- a/cargo-psp/src/fix_imports.rs +++ b/cargo-psp/src/fix_imports.rs @@ -1,5 +1,5 @@ -use std::{mem, path::Path, collections::HashMap}; use goblin::elf::Elf; +use std::{collections::HashMap, mem, path::Path}; /// A stub library entry like the one found in the `psp` crate, but with types /// changed to be cross-platform. @@ -23,7 +23,7 @@ struct SceStubLibraryEntry { /// completely stripped. This can happen with LTO, for example. Because of this, /// we need a way to write the stub count post-linking. This function does /// exactly this. -pub fn fix<T: AsRef<Path>>(path: T) { +pub fn fix(path: &Path) { let mut bytes = std::fs::read(&path).unwrap(); let elf = Elf::parse(&bytes).unwrap(); @@ -36,7 +36,8 @@ pub fn fix<T: AsRef<Path>>(path: T) { }; // Map of name -> section header - let sections = elf.section_headers + let sections = elf + .section_headers .iter() .map(|sh| { let name = shstrtab[sh.sh_name..] @@ -60,10 +61,12 @@ pub fn fix<T: AsRef<Path>>(path: T) { // be stripped. // If we have .lib.stub, then .lib.stub.btm must exist. - let lib_stub_btm = sections.get(".lib.stub.btm") + let lib_stub_btm = sections + .get(".lib.stub.btm") .expect("could not find .lib.stub.btm section"); - let rodata_sce_nid = sections.get(".rodata.sceNid") + let rodata_sce_nid = sections + .get(".rodata.sceNid") .expect("Could not find .rodata.sceNid section"); let start = lib_stub.sh_offset as usize; @@ -89,21 +92,16 @@ pub fn fix<T: AsRef<Path>>(path: T) { }; // Iterator of mutable byte slices (of stub lib entries) in raw ELF binary. - let stub_entry_bufs = bytes[start..end] - .chunks_mut(mem::size_of::<SceStubLibraryEntry>()); + let stub_entry_bufs = bytes[start..end].chunks_mut(mem::size_of::<SceStubLibraryEntry>()); for (i, stub_entry_buf) in stub_entry_bufs.enumerate() { // A NID is a 32-bit value. const NID_SIZE: u32 = 4; - let mut stub: SceStubLibraryEntry = bincode::deserialize(stub_entry_buf) - .unwrap(); + let mut stub: SceStubLibraryEntry = bincode::deserialize(stub_entry_buf).unwrap(); - let nid_end = stubs_nid_sorted.get( - 1 + stubs_nid_sorted.iter() - .position(|&(j, _)| i == j) - .unwrap() - ) + let nid_end = stubs_nid_sorted + .get(1 + stubs_nid_sorted.iter().position(|&(j, _)| i == j).unwrap()) .map(|(_, s)| s.nid_table) .unwrap_or(rodata_sce_nid.sh_addr as u32 + rodata_sce_nid.sh_size as u32); diff --git a/cargo-psp/src/main.rs b/cargo-psp/src/main.rs index 606518af..43a94111 100644 --- a/cargo-psp/src/main.rs +++ b/cargo-psp/src/main.rs @@ -1,7 +1,7 @@ -use cargo_metadata::MetadataCommand; -use rustc_version::{Version, Channel}; +use cargo_metadata::Message as CargoMessage; +use rustc_version::{Channel, Version}; use std::{ - env, fs, fmt, + env, fmt, fs, io::ErrorKind, process::{self, Command, Stdio}, }; @@ -104,7 +104,7 @@ struct CommitDate { impl CommitDate { fn parse(date: &str) -> Option<Self> { - let mut iter = date.split("-"); + let mut iter = date.split('-'); let year = iter.next()?.parse().ok()?; let month = iter.next()?.parse().ok()?; @@ -123,7 +123,11 @@ impl fmt::Display for CommitDate { // Minimum 2022-06-11, remember to update both commit date and version too, // below. Note that the `day` field lags by one day, as the toolchain always // contains the previous days' nightly rustc. -const MINIMUM_COMMIT_DATE: CommitDate = CommitDate { year: 2022, month: 06, day: 10 }; +const MINIMUM_COMMIT_DATE: CommitDate = CommitDate { + year: 2022, + month: 6, + day: 10, +}; const MINIMUM_RUSTC_VERSION: Version = Version { major: 1, minor: 63, @@ -144,16 +148,19 @@ fn main() { process::exit(1); } - let old_version = MINIMUM_RUSTC_VERSION > Version { - // Remove `-nightly` pre-release tag for comparison. - pre: Vec::new(), - ..rustc_version.semver.clone() - }; + let old_version = MINIMUM_RUSTC_VERSION + > Version { + // Remove `-nightly` pre-release tag for comparison. + pre: Vec::new(), + ..rustc_version.semver.clone() + }; let old_commit = match rustc_version.commit_date { None => false, - Some(date) => MINIMUM_COMMIT_DATE > CommitDate::parse(&date) - .expect("could not parse `rustc --version` commit date"), + Some(date) => { + MINIMUM_COMMIT_DATE + > CommitDate::parse(&date).expect("could not parse `rustc --version` commit date") + } }; if old_version || old_commit { @@ -161,9 +168,7 @@ fn main() { "cargo-psp requires rustc nightly version >= {}", MINIMUM_COMMIT_DATE, ); - println!( - "Please run `rustup update nightly` to upgrade your nightly version" - ); + println!("Please run `rustup update nightly` to upgrade your nightly version"); process::exit(1); } @@ -189,10 +194,8 @@ fn main() { Ok(_) => { eprintln!("[NOTE]: Detected RUST_PSP_BUILD_STD env var, using \"build-std\"."); "build-std" - }, - Err(_) => { - "build-std=core,compiler_builtins,alloc,panic_unwind,panic_abort" - }, + } + Err(_) => "build-std=core,compiler_builtins,alloc,panic_unwind,panic_abort", }; let mut process = Command::new("cargo") @@ -201,125 +204,106 @@ fn main() { .arg(build_std_flag) .arg("--target") .arg("mipsel-sony-psp") + .arg("--message-format=json-render-diagnostics") .args(args) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) + .stdout(Stdio::piped()) .spawn() .unwrap(); + let reader = std::io::BufReader::new(process.stdout.take().unwrap()); + let executables: Vec<_> = CargoMessage::parse_stream(reader) + .flat_map(|msg| match msg.unwrap() { + CargoMessage::CompilerArtifact(art) => art.executable, + _ => None, + }) + .collect(); + let status = process.wait().unwrap(); if !status.success() { - let code = match status.code() { - Some(i) => i, - None => 1, - }; - - process::exit(code); + process::exit(status.code().unwrap_or(1)); } - let metadata = MetadataCommand::new() - .exec() - .expect("failed to get cargo metadata"); - - // Is there a better way to do this? - let profile_name = if env::args().any(|arg| arg == "--release") { - "release" - } else { - "debug" - }; - - let bin_dir = metadata - .target_directory - .join("mipsel-sony-psp") - .join(profile_name); - - for id in metadata.clone().workspace_members { - let package = metadata[&id].clone(); - - // TODO: Error if no bin is ever found. - for target in package.targets { - if target.kind.iter().any(|k| k == "bin") { - let elf_path = bin_dir.join(&target.name); - let prx_path = bin_dir.join(target.name.clone() + ".prx"); - - let sfo_path = bin_dir.join("PARAM.SFO"); - let pbp_path = bin_dir.join("EBOOT.PBP"); - - fix_imports::fix(&elf_path); - - Command::new("prxgen") - .arg(&elf_path) - .arg(&prx_path) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("failed to run prxgen"); - - let config_args = vec![ - ("-s", "DISC_ID", config.disc_id.clone()), - ("-s", "DISC_VERSION", config.disc_version.clone()), - ("-s", "LANGUAGE", config.language.clone()), - ("-d", "PARENTAL_LEVEL", config.parental_level.as_ref().map(u32::to_string)), - ("-s", "PSP_SYSTEM_VER", config.psp_system_ver.clone()), - ("-d", "REGION", config.region.as_ref().map(u32::to_string)), - ("-s", "TITLE_0", config.title_jp.clone()), - ("-s", "TITLE_2", config.title_fr.clone()), - ("-s", "TITLE_3", config.title_es.clone()), - ("-s", "TITLE_4", config.title_de.clone()), - ("-s", "TITLE_5", config.title_it.clone()), - ("-s", "TITLE_6", config.title_nl.clone()), - ("-s", "TITLE_7", config.title_pt.clone()), - ("-s", "TITLE_8", config.title_ru.clone()), - ("-s", "UPDATER_VER", config.updater_version.clone()), - ]; - - Command::new("mksfo") - // Add the optional config args - .args({ - config_args - .into_iter() - - // Filter through all the values that are not `None` - .filter_map(|(f, k, v)| v.map(|v| (f, k, v))) - - // Map into 2 arguments, e.g. "-s" "NAME=VALUE" - .flat_map(|(flag, key, value)| vec![ - flag.into(), - format!("{}={}", key, value), - ]) - }) - .arg(config.title.clone().unwrap_or(target.name)) - .arg(&sfo_path) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("failed to run mksfo"); - - Command::new("pack-pbp") - .arg(&pbp_path) - .arg(&sfo_path) - .arg(config.xmb_icon_png.clone().unwrap_or("NULL".into())) - .arg(config.xmb_icon_pmf.clone().unwrap_or("NULL".into())) - .arg( - config - .xmb_background_overlay_png - .clone() - .unwrap_or("NULL".into()), - ) - .arg(config.xmb_background_png.clone().unwrap_or("NULL".into())) - .arg(config.xmb_music_at3.clone().unwrap_or("NULL".into())) - .arg(&prx_path) - .arg(config.psar.clone().unwrap_or("NULL".into())) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("failed to run pack-pbp"); - } - } + // TODO: Warn if no bin is ever found. + for elf_path in executables { + let prx_path = elf_path.with_extension("prx"); + + let sfo_path = elf_path.with_extension("param.sfo"); + let pbp_path = elf_path.with_extension("eboot.pbp"); + + fix_imports::fix(&elf_path); + + Command::new("prxgen") + .arg(&elf_path) + .arg(&prx_path) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output() + .expect("failed to run prxgen"); + + let config_args = vec![ + ("-s", "DISC_ID", config.disc_id.clone()), + ("-s", "DISC_VERSION", config.disc_version.clone()), + ("-s", "LANGUAGE", config.language.clone()), + ( + "-d", + "PARENTAL_LEVEL", + config.parental_level.as_ref().map(u32::to_string), + ), + ("-s", "PSP_SYSTEM_VER", config.psp_system_ver.clone()), + ("-d", "REGION", config.region.as_ref().map(u32::to_string)), + ("-s", "TITLE_0", config.title_jp.clone()), + ("-s", "TITLE_2", config.title_fr.clone()), + ("-s", "TITLE_3", config.title_es.clone()), + ("-s", "TITLE_4", config.title_de.clone()), + ("-s", "TITLE_5", config.title_it.clone()), + ("-s", "TITLE_6", config.title_nl.clone()), + ("-s", "TITLE_7", config.title_pt.clone()), + ("-s", "TITLE_8", config.title_ru.clone()), + ("-s", "UPDATER_VER", config.updater_version.clone()), + ]; + + Command::new("mksfo") + // Add the optional config args + .args({ + config_args + .into_iter() + // Filter through all the values that are not `None` + .filter_map(|(f, k, v)| v.map(|v| (f, k, v))) + // Map into 2 arguments, e.g. "-s" "NAME=VALUE" + .flat_map(|(flag, key, value)| vec![flag.into(), format!("{}={}", key, value)]) + }) + .arg( + config + .title + .as_ref() + .map(|s| s.as_ref()) + .unwrap_or_else(|| elf_path.file_stem().unwrap()), + ) + .arg(&sfo_path) + .status() + .expect("failed to run mksfo"); + + Command::new("pack-pbp") + .arg(&pbp_path) + .arg(&sfo_path) + .arg(config.xmb_icon_png.as_deref().unwrap_or("NULL")) + .arg(config.xmb_icon_pmf.as_deref().unwrap_or("NULL")) + .arg( + config + .xmb_background_overlay_png + .as_deref() + .unwrap_or("NULL"), + ) + .arg(config.xmb_background_png.as_deref().unwrap_or("NULL")) + .arg(config.xmb_music_at3.as_deref().unwrap_or("NULL")) + .arg(&prx_path) + .arg(config.psar.as_deref().unwrap_or("NULL")) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output() + .expect("failed to run pack-pbp"); } } diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 00000000..4f10fc34 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["*"] +exclude = ["target"] diff --git a/examples/vfpu-context-switching/src/main.rs b/examples/vfpu-context-switching/src/main.rs index 8dd4b564..740c1f81 100644 --- a/examples/vfpu-context-switching/src/main.rs +++ b/examples/vfpu-context-switching/src/main.rs @@ -8,7 +8,7 @@ psp::module!("vfpu_context_test", 1, 1); #[no_mangle] #[inline(never)] -extern fn psp_main() { +fn psp_main() { psp::enable_home_button(); psp::dprintln!("Testing VFPU context switcher...");