diff --git a/Cargo.lock b/Cargo.lock index fd759b7..195ab27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "aes" version = "0.3.2" @@ -88,6 +90,11 @@ dependencies = [ "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "0.9.1" @@ -141,6 +148,14 @@ dependencies = [ "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "blz-nx" +version = "0.1.0" +source = "git+https://github.com/Thog/blz-nx-rs#7e2dccf2a3e1ccb52408e1db995e8f3971de4f2c" +dependencies = [ + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "byte-tools" version = "0.2.0" @@ -173,9 +188,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -185,9 +200,9 @@ source = "git+https://github.com/roblabla/cargo_metadata#8e2f400d2464f4bd4b5f900 dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -275,11 +290,6 @@ dependencies = [ "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "dtoa" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "elf" version = "0.0.10" @@ -394,7 +404,7 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -407,7 +417,9 @@ name = "linkle" version = "0.2.7" dependencies = [ "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "block-modes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blz-nx 0.1.0 (git+https://github.com/Thog/blz-nx-rs)", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "cargo_metadata 0.6.0 (git+https://github.com/roblabla/cargo_metadata)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -421,9 +433,9 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -570,6 +582,11 @@ dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ryu" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "same-file" version = "1.0.3" @@ -607,7 +624,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -617,27 +634,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.70" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.70" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.24" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -661,7 +678,7 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -850,6 +867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" +"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" @@ -857,6 +875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-modes 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "283fa06a14026feac8912bf35328fc074f5d68907fd4b9cccad5658a3fc62a30" "checksum block-padding 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc4358306e344bf9775d0197fd00d2603e5afb0771bb353538630f022068ea3" +"checksum blz-nx 0.1.0 (git+https://github.com/Thog/blz-nx-rs)" = "" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" "checksum bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8" @@ -874,7 +893,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum derive_more 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f57d78cf3bd45270dad4e70c21ec77a960b36c7a841ff9db76aaa775a8fb871" "checksum digest 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b29c278aa8fd30796bd977169e8004b4aa88cdcd2f32a6eb22bc2d5d38df94a" "checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" -"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" @@ -889,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2" "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)" = "413f3dfc802c5dc91dc570b05125b6cda9855edfaa9825c9849807876376e70e" "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" "checksum lz4 1.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43c94a9f09a60017f373020cc93d4291db4cd92b0db64ff25927f27d09dc23d5" @@ -911,15 +929,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66f024a8cc5e456eb870f688dbd899c84f61190c82c7a911e40f926941969074" "checksum scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f9a353f5dd99e42ff097d5a61db3257aa2c7127d76a3fa8287b642ef9ae0f7c5" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" -"checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124" -"checksum serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c6908c7b925cd6c590358a4034de93dbddb20c45e1d021931459fd419bf0e2" +"checksum serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "a72e9b96fa45ce22a4bc23da3858dfccfd60acd28a25bcd328a98fdd6bea43fd" +"checksum serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)" = "101b495b109a3e3ca8c4cbe44cf62391527cdfb6ba15821c5ce80bcd5ea23f9f" +"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4474d6da9593171bcb086890fc344a3a12783cb24e5b141f8a5d0e43561f4b6" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" diff --git a/Cargo.toml b/Cargo.toml index 895761f..8de395c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ aes = "0.3" num-traits = "0.2" derive_more = "0.13" cmac = "0.2.0" +blz-nx = { git = "https://github.com/Thog/blz-nx-rs" } +bit_field = "0.10.0" [features] binaries = ["structopt", "cargo_metadata", "scroll", "goblin", "clap"] diff --git a/src/bin/linkle_clap.rs b/src/bin/linkle_clap.rs index d5326ea..843a0cb 100644 --- a/src/bin/linkle_clap.rs +++ b/src/bin/linkle_clap.rs @@ -40,6 +40,16 @@ enum Opt { /// Sets the output file to use. output_file: String, }, + /// Create a KIP file from an ELF and an NPDM file. + #[structopt(name = "kip")] + Kip { + /// Sets the input ELF file to use. + input_file: String, + /// Sets the input NPDM JSON file to use. + npdm_file: String, + /// Sets the output file to use. + output_file: String, + }, /// Create a PFS0 or NSP file from a directory. #[structopt(name = "pfs0"/*, raw(alias = "nsp")*/)] Pfs0 { @@ -117,6 +127,18 @@ fn create_nxo(format: &str, input_file: &str, output_file: &str, icon_file: Opti Ok(()) } +fn create_kip(input_file: &str, npdm_file: &str, output_file: &str) -> Result<(), linkle::error::Error> { + let mut nxo = linkle::format::nxo::NxoFile::from_elf(&input_file).map_err(|err| (err, &input_file))?; + let npdm = serde_json::from_reader(File::open(npdm_file).map_err(|err| (err, npdm_file))?)?; + + let mut option = OpenOptions::new(); + let output_option = option.write(true).create(true).truncate(true); + output_option.open(output_file)?; + + nxo.write_kip1(&mut output_option.open(output_file).map_err(|err| (err, output_file))?, &npdm).map_err(|err| (err, output_file))?; + Ok(()) +} + fn create_pfs0(input_directory: &str, output_file: &str) -> Result<(), linkle::error::Error> { let mut pfs0 = linkle::format::pfs0::Pfs0::from_directory(&input_directory)?; let mut option = OpenOptions::new(); @@ -179,6 +201,7 @@ fn process_args(app: &Opt) { let res = match app { Opt::Nro { ref input_file, ref output_file, ref icon, ref romfs, ref nacp } => create_nxo("nro", input_file, output_file, to_opt_ref(icon), to_opt_ref(romfs), to_opt_ref(nacp)), Opt::Nso { ref input_file, ref output_file } => create_nxo("nso", input_file, output_file, None, None, None), + Opt::Kip { ref input_file, ref npdm_file, ref output_file } => create_kip(input_file, npdm_file, output_file), Opt::Pfs0 { ref input_directory, ref output_file } => create_pfs0(input_directory, output_file), Opt::Pfs0Extract { ref input_file, ref output_directory } => extract_pfs0(input_file, output_directory), Opt::Nacp { ref input_file, ref output_file } => create_nacp(input_file, output_file), diff --git a/src/error.rs b/src/error.rs index a345ce1..9e2195e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,8 @@ use derive_more::Display; #[derive(Debug, Fail, Display)] pub enum Error { + #[display(fmt = "Failed to deserialize: {}", _0)] + Deserialization(#[cause] serde_json::error::Error), #[display(fmt = "{}: {}", "_1.display()", _0)] Io(#[cause] io::Error, PathBuf, Backtrace), #[display(fmt = "Internal IO Error (please submit a bug report with the backtrace): {}", _0)] @@ -76,6 +78,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: serde_json::error::Error) -> Error { + Error::Deserialization(err) + } +} + impl From for Error { fn from(err: FromUtf8Error) -> Error { // Why the heck does OsStr not have display()? diff --git a/src/format/mod.rs b/src/format/mod.rs index b02ca7f..e921d5c 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -2,4 +2,5 @@ pub mod nacp; pub mod nxo; pub mod pfs0; pub mod romfs; +mod npdm; mod utils; diff --git a/src/format/npdm.rs b/src/format/npdm.rs new file mode 100644 index 0000000..f301a7d --- /dev/null +++ b/src/format/npdm.rs @@ -0,0 +1,120 @@ +use byteorder::{LittleEndian, WriteBytesExt}; +use crate::format::utils; +use std; +use std::fmt; +use std::fs::File; +use std::io::Write; +use std::collections::HashMap; +use crate::format::utils::HexOrNum; +use serde_derive::{Serialize, Deserialize}; +use serde_json; +use bit_field::BitField; +use std::convert::TryFrom; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type", content = "value")] +#[serde(rename_all = "snake_case")] +pub enum KernelCapability { + KernelFlags { + highest_thread_priority: u8, + lowest_thread_priority: u8, + highest_cpu_id: u8, + lowest_cpu_id: u8, + }, + Syscalls(HashMap), + Map { + address: HexOrNum, + size: HexOrNum, + is_ro: bool, + is_io: bool, + }, + MapPage(HexOrNum), + IrqPair([u16; 2]), + ApplicationType(u16), + MinKernelVersion(HexOrNum), + HandleTableSize(u16), + DebugFlags { + allow_debug: bool, + force_debug: bool, + }, +} + +impl KernelCapability { + pub fn encode(&self) -> Vec { + match self { + KernelCapability::KernelFlags { + highest_thread_priority, + lowest_thread_priority, + highest_cpu_id, + lowest_cpu_id, + } => { + vec![*0b111u32 + .set_bits(04..10, u32::from(*lowest_thread_priority)) + .set_bits(10..16, u32::from(*highest_thread_priority)) + .set_bits(16..24, u32::from(*lowest_cpu_id)) + .set_bits(24..32, u32::from(*highest_cpu_id))] + }, + KernelCapability::Syscalls(syscalls) => { + let mut masks = vec![0b1111u32; 6]; + let mut used = [false; 6]; + for (idx, mask) in masks.iter_mut().enumerate() { + mask.set_bits(29..32, idx as u32); + } + for (syscall_name, syscall_val) in syscalls { + masks[syscall_val.0 as usize / 24].set_bit(usize::try_from((syscall_val.0 % 24) + 5).unwrap(), true); + used[syscall_val.0 as usize / 24] = true; + } + for (idx, used) in used.iter().enumerate().rev() { + if !used { + masks.remove(idx); + } + } + masks + }, + KernelCapability::Map { + address, + size, + is_ro, + is_io, + } => { + let mut val = vec![0b111111u32, 0b111111u32]; + val[0] + .set_bits(7..31, u32::try_from(address.0).unwrap()) + .set_bit(31, *is_ro); + val[1] + .set_bits(7..31, u32::try_from(size.0).unwrap()) + .set_bit(31, *is_io); + val + }, + KernelCapability::MapPage(page) => { + vec![*0b1111111u32 + .set_bits(8..32, u32::try_from(page.0).unwrap())] + }, + KernelCapability::IrqPair(irq_pair) => { + vec![*0b11111111111u32 + .set_bits(12..22, u32::from(irq_pair[0])) + .set_bits(22..32, u32::from(irq_pair[1]))] + }, + KernelCapability::ApplicationType(app_type) => { + vec![*0b1111111111111u32 + .set_bits(14..17, u32::from(*app_type))] + }, + KernelCapability::MinKernelVersion(min_kernel) => { + vec![*0b11111111111111u32 + .set_bits(15..32, u32::try_from(min_kernel.0).unwrap())] + }, + KernelCapability::HandleTableSize(handle_table_size) => { + vec![*0b111111111111111u32 + .set_bits(16..26, u32::from(*handle_table_size))] + }, + KernelCapability::DebugFlags { + allow_debug, + force_debug, + } => { + vec![*0b1111111111111111u32 + .set_bit(17, *allow_debug) + .set_bit(18, *force_debug)] + }, + } + } +} diff --git a/src/format/nxo.rs b/src/format/nxo.rs index 81634c9..6dd9d21 100644 --- a/src/format/nxo.rs +++ b/src/format/nxo.rs @@ -1,16 +1,20 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use elf; -use elf::types::{EM_ARM, EM_AARCH64, ProgramHeader, PT_LOAD, SHT_NOTE}; -use crate::format::{utils, romfs::RomFs, nacp::NacpFile}; +use elf::types::{EM_ARM, EM_AARCH64, ProgramHeader, PT_LOAD, SHT_NOTE, Machine}; +use crate::format::{utils, romfs::RomFs, nacp::NacpFile, npdm::KernelCapability}; use std; use std::fs::File; use std::io::{self, Cursor, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process; +use serde_derive::{Serialize, Deserialize}; +use crate::format::utils::HexOrNum; +use std::convert::TryFrom; // TODO: Support switchbrew's embedded files for NRO pub struct NxoFile { file: File, + machine: Machine, text_section: ProgramHeader, rodata_section: ProgramHeader, data_section: ProgramHeader, @@ -18,6 +22,18 @@ pub struct NxoFile { build_id: Option>, } +#[derive(Debug, Serialize, Deserialize)] +pub struct KipNpdm { + name: String, + title_id: HexOrNum, + main_thread_stack_size: HexOrNum, + main_thread_priority: u8, + default_cpu_id: u8, + process_category: u8, + flags: Option, + kernel_capabilities: Vec, +} + fn pad_segment(previous_segment_data: &mut Vec, offset: usize, section: &ProgramHeader) { let section_vaddr = section.vaddr as usize; let section_supposed_start = previous_segment_data.len() + offset; @@ -111,6 +127,7 @@ impl NxoFile { Ok(NxoFile { file, + machine: elf_file.ehdr.machine, text_section: *text_section, rodata_section: *rodata_section, data_section: *data_section, @@ -406,4 +423,100 @@ impl NxoFile { output_writter.write_all(&compressed_data)?; Ok(()) } + + pub fn write_kip1(&mut self, output_writer: &mut T, npdm: &KipNpdm) -> std::io::Result<()> + where + T: Write, + { + output_writer.write_all(b"KIP1")?; + let mut name : Vec = npdm.name.clone().into(); + name.resize(12, 0); + output_writer.write_all(&name[..])?; + output_writer.write_u64::(npdm.title_id.0)?; // TitleId + output_writer.write_u32::(u32::from(npdm.process_category))?; + output_writer.write_u8(npdm.main_thread_priority)?; + output_writer.write_u8(npdm.default_cpu_id)?; + output_writer.write_u8(0)?; // Reserved + if let Some(flags) = npdm.flags { + output_writer.write_u8(flags)?; + } else if self.machine == EM_AARCH64 { + // Compression enable, Is64Bit, IsAddrSpace32Bit, UseSystemPoolPartition + output_writer.write_u8(0b00111111)?; + } else if self.machine == EM_ARM { + // Compression enable, UseSystemPoolPartition + output_writer.write_u8(0b00100111)?; + } else { + unimplemented!("Unknown machine type"); + } + + let mut section_data = utils::get_section_data(&mut self.file, &self.text_section)?; + let text_data = utils::compress_blz(&mut section_data).unwrap(); + let mut section_data = utils::get_section_data(&mut self.file, &self.rodata_section)?; + let rodata_data = utils::compress_blz(&mut section_data).unwrap(); + let mut section_data = utils::get_section_data(&mut self.file, &self.data_section)?; + let data_data = utils::compress_blz(&mut section_data).unwrap(); + + write_kip_section_header(output_writer, &self.text_section, 0, text_data.len() as u32)?; + write_kip_section_header(output_writer, &self.rodata_section, u32::try_from(npdm.main_thread_stack_size.0).expect("Exected main_thread_stack_size to be an u32"), rodata_data.len() as u32)?; + write_kip_section_header(output_writer, &self.data_section, 0, data_data.len() as u32)?; + + if let Some(section) = self.bss_section { + output_writer.write_u32::(u32::try_from(section.vaddr).expect("BSS vaddr too big"))?; + output_writer.write_u32::(u32::try_from(section.memsz).expect("BSS memsize too big"))?; + } else { + // in this case the bss is missing or is embedeed in .data. libnx does that, let's support it + let data_section_size = (self.data_section.filesz + 0xFFF) & !0xFFF; + let bss_size = if self.data_section.memsz > data_section_size { + (((self.data_section.memsz - data_section_size) + 0xFFF) & !0xFFF) as u32 + } else { + 0 + }; + output_writer.write_u32::(u32::try_from(self.data_section.vaddr + data_section_size).unwrap())?; + output_writer.write_u32::(bss_size)?; + } + output_writer.write_u32::(0)?; + output_writer.write_u32::(0)?; + + // Empty Sections: + for i in 4..6 { + output_writer.write_u32::(0)?; + output_writer.write_u32::(0)?; + output_writer.write_u32::(0)?; + output_writer.write_u32::(0)?; + } + + // Kernel caps: + let caps = npdm.kernel_capabilities.iter() + .map(|v| v.encode()) + .flatten() + .collect::>(); + assert!(caps.len() < 0x20, "kernel_capabilities should have less than 0x20 entries!"); + + unsafe { + // Safety: This is safe. I'm just casting a slice of u32 to a slice of u8 + // for fuck's sake. + output_writer.write_all(std::slice::from_raw_parts(caps.as_ptr() as *const u8, caps.len() * 4))?; + } + + output_writer.write_all(&vec![0xFF; (0x20 - caps.len()) * 4])?; + + // Section data + output_writer.write_all(&text_data); + output_writer.write_all(&rodata_data); + output_writer.write_all(&data_data); + + Ok(()) + } +} + +pub fn write_kip_section_header(output_writer: &mut T, section: &ProgramHeader, attributes: u32, compressed_size: u32) -> std::io::Result<()> +where + T: Write, +{ + output_writer.write_u32::(u32::try_from(section.vaddr).expect("vaddr too big"))?; + output_writer.write_u32::(u32::try_from(section.filesz).expect("memsz too big"))?; + output_writer.write_u32::(u32::try_from(compressed_size).expect("Compressed size too big"))?; + output_writer.write_u32::(attributes)?; + + Ok(()) } diff --git a/src/format/utils.rs b/src/format/utils.rs index 811c81b..94246ad 100644 --- a/src/format/utils.rs +++ b/src/format/utils.rs @@ -1,9 +1,12 @@ use elf; use lz4; use std; +use std::fmt; use std::fs::File; use std::io::{Read, Seek, SeekFrom}; use sha2::{Sha256, Digest}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; +use serde::de::{Visitor, Unexpected}; pub fn align(size: usize, padding: usize) -> usize { ((size as usize) + padding) & !padding @@ -35,8 +38,73 @@ pub fn compress_lz4(uncompressed_data: &mut Vec) -> std::io::Result> lz4::block::compress(&uncompressed_data[..], None, false) } +pub fn compress_blz(uncompressed_data: &mut Vec) -> blz_nx::BlzResult> { + let mut compressed_data = vec![0; blz_nx::get_worst_compression_buffer_size(uncompressed_data.len())]; + let res = blz_nx::compress_raw(&mut uncompressed_data[..], &mut compressed_data[..])?; + compressed_data.resize(res, 0); + Ok(compressed_data) +} + pub fn calculate_sha256(data: &[u8]) -> std::io::Result> { let mut hasher = Sha256::default(); hasher.input(data); Ok(Vec::from(hasher.result().as_slice())) } + +#[derive(Default)] +pub struct HexOrNum(pub u64); + +impl fmt::Debug for HexOrNum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("{:#010x}", self.0)) + } +} + +impl<'de> Deserialize<'de> for HexOrNum { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct HexOrNumVisitor; + + impl<'a> Visitor<'a> for HexOrNumVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "an integer or a hex-formatted string") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error + { + Ok(v) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error + { + if (v.starts_with("0x")) { + u64::from_str_radix(&v[2..], 16).map_err(|_| { + E::invalid_value(Unexpected::Str(v), &"a hex-encoded string") + }) + } else { + Err(E::invalid_value(Unexpected::Str(v), &"a hex-encoded string")) + } + } + } + + let num = Deserializer::deserialize_any(deserializer, HexOrNumVisitor)?; + Ok(HexOrNum(num)) + } +} + +impl Serialize for HexOrNum { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_str(&format_args!("{:#010x}", self.0)) + } +}