From 6d21243343377ab439c84d90c1a06e4ecb813313 Mon Sep 17 00:00:00 2001 From: Pika <15848969+ThatNerdyPikachu@users.noreply.github.com> Date: Mon, 24 Dec 2018 11:49:59 -0500 Subject: [PATCH 1/5] cnmt: init --- src/format/cnmt.rs | 174 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/format/cnmt.rs diff --git a/src/format/cnmt.rs b/src/format/cnmt.rs new file mode 100644 index 0000000..2760b6a --- /dev/null +++ b/src/format/cnmt.rs @@ -0,0 +1,174 @@ +use std::fs::File; +use std::io::{Seek, SeekFrom, Read}; +use byteorder::{LittleEndian, ReadBytesExt}; + +pub struct Cnmt { + title_id: u64, + title_version: u32, + title_type: TitleType, + table_offset: u16, + content_entry_count: u16, + meta_entry_count: u16, + title_header: Option /* TODO: figure out a better way to do this with the different title types. */, + content_entries: Vec, + meta_entries: Option> +} + +#[derive(Clone)] +pub enum TitleType { + SystemProgram, + SystemData, + SystemUpdate, + BootImagePackage, + BootImagePackageSafe, + Application, + Patch, + AddOnContent, + Delta, + Unknown +} + +pub struct TitleHeader { + title_id: u64, + minimum_version: u32 +} + +pub struct ContentEntry { + hash: [u8; 32], + nca_id: [u8; 16], + size: u64, + content_type: ContentType +} + +pub enum ContentType { + Meta, + Program, + Data, + Control, + HtmlDocument, + LegalInformation, + DeltaFragment, + Unknown +} + +#[derive(Clone)] +pub struct MetaEntry { + title_id: u64, + title_version: u32, + title_type: TitleType +} + +impl Cnmt { + pub fn from_file(mut file: &File) -> std::io::Result { + let title_id = file.read_u64::()?; + + let title_version = file.read_u32::()?; + + let title_type = TitleType::from_u8(&file.read_u8()?); + + file.read_u8()?; + + let table_offset = file.read_u16::()?; + + let content_entry_count = file.read_u16::()?; + + let meta_entry_count = file.read_u16::()?; + + file.seek(SeekFrom::Current(12))?; + + let mut title_header = None; + + match title_type { + TitleType::Application | TitleType::Patch | TitleType::AddOnContent => { + title_header = Some(TitleHeader { + title_id: file.read_u64::()?, + minimum_version: file.read_u32::()? + }); + }, + _ => {} + } + + file.seek(SeekFrom::Start((0x20 + table_offset) as u64))?; + + let mut content_entries = Vec::new(); + + for _ in 0..content_entry_count { + let mut hash = [0; 32]; + + file.read_exact(&mut hash)?; + + let mut nca_id = [0; 16]; + + file.read_exact(&mut nca_id)?; + + content_entries.push(ContentEntry { + hash, + nca_id, + size: file.read_u48::()?, + content_type: ContentType::from_u8(&file.read_u8()?) + }); + + file.read_u8()?; + } + + let mut meta_entries = None; + + if meta_entry_count != 0 { + meta_entries = Some(Vec::new()); + + for _ in 0..meta_entry_count { + meta_entries.clone().unwrap().push(MetaEntry { + title_id: file.read_u64::()?, + title_version: file.read_u32::()?, + title_type: TitleType::from_u8(&file.read_u8()?) + }); + + file.read_u24::()?; + } + } + + Ok(Cnmt { + title_id, + title_version, + title_type, + table_offset, + content_entry_count, + meta_entry_count, + title_header, + content_entries, + meta_entries + }) + } +} + +impl TitleType { + fn from_u8(title_type: &u8) -> Self { + match title_type { + 0x01 => TitleType::SystemProgram, + 0x02 => TitleType::SystemData, + 0x03 => TitleType::SystemUpdate, + 0x04 => TitleType::BootImagePackage, + 0x05 => TitleType::BootImagePackageSafe, + 0x80 => TitleType::Application, + 0x81 => TitleType::Patch, + 0x82 => TitleType::AddOnContent, + 0x83 => TitleType::Delta, + _ => TitleType::Unknown + } + } +} + +impl ContentType { + fn from_u8(content_type: &u8) -> Self { + match content_type { + 00 => ContentType::Meta, + 01 => ContentType::Program, + 02 => ContentType::Data, + 03 => ContentType::Control, + 04 => ContentType::HtmlDocument, + 05 => ContentType::LegalInformation, + 06 => ContentType::DeltaFragment, + _ => ContentType::Unknown + } + } +} From 830e1383e742deb3a0115d0874092dab8c804a78 Mon Sep 17 00:00:00 2001 From: Pika <15848969+ThatNerdyPikachu@users.noreply.github.com> Date: Mon, 24 Dec 2018 11:50:42 -0500 Subject: [PATCH 2/5] cnmt: add to format module --- src/format/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/format/mod.rs b/src/format/mod.rs index b02ca7f..f779ea4 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; +pub mod cnmt; mod utils; From 506412386bc3543cb8b40d9f55f1ee6ea1edcb41 Mon Sep 17 00:00:00 2001 From: Pika <15848969+ThatNerdyPikachu@users.noreply.github.com> Date: Mon, 24 Dec 2018 12:30:20 -0500 Subject: [PATCH 3/5] cnmt: fix meta entry parsing --- src/format/cnmt.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/format/cnmt.rs b/src/format/cnmt.rs index 2760b6a..6bf5cc8 100644 --- a/src/format/cnmt.rs +++ b/src/format/cnmt.rs @@ -11,10 +11,9 @@ pub struct Cnmt { meta_entry_count: u16, title_header: Option /* TODO: figure out a better way to do this with the different title types. */, content_entries: Vec, - meta_entries: Option> + meta_entries: Vec } -#[derive(Clone)] pub enum TitleType { SystemProgram, SystemData, @@ -51,7 +50,6 @@ pub enum ContentType { Unknown } -#[derive(Clone)] pub struct MetaEntry { title_id: u64, title_version: u32, @@ -111,20 +109,16 @@ impl Cnmt { file.read_u8()?; } - let mut meta_entries = None; + let mut meta_entries = Vec::new(); - if meta_entry_count != 0 { - meta_entries = Some(Vec::new()); - - for _ in 0..meta_entry_count { - meta_entries.clone().unwrap().push(MetaEntry { - title_id: file.read_u64::()?, - title_version: file.read_u32::()?, - title_type: TitleType::from_u8(&file.read_u8()?) - }); + for _ in 0..meta_entry_count { + meta_entries.push(MetaEntry { + title_id: file.read_u64::()?, + title_version: file.read_u32::()?, + title_type: TitleType::from_u8(&file.read_u8()?) + }); - file.read_u24::()?; - } + file.read_u24::()?; } Ok(Cnmt { From 30600455823553decb5a0b2be416767d7b1f4594 Mon Sep 17 00:00:00 2001 From: Pika <15848969+ThatNerdyPikachu@users.noreply.github.com> Date: Mon, 24 Dec 2018 13:03:44 -0500 Subject: [PATCH 4/5] cnmt: use match for TitleHeader --- src/format/cnmt.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/format/cnmt.rs b/src/format/cnmt.rs index 6bf5cc8..06ec63c 100644 --- a/src/format/cnmt.rs +++ b/src/format/cnmt.rs @@ -74,17 +74,14 @@ impl Cnmt { file.seek(SeekFrom::Current(12))?; - let mut title_header = None; - - match title_type { + let title_header = match title_type { TitleType::Application | TitleType::Patch | TitleType::AddOnContent => { - title_header = Some(TitleHeader { + Some(TitleHeader { title_id: file.read_u64::()?, minimum_version: file.read_u32::()? - }); - }, - _ => {} - } + }), + _ => None + }; file.seek(SeekFrom::Start((0x20 + table_offset) as u64))?; From fb7c61126d6cece43ae487895916d6ce3cf37fa3 Mon Sep 17 00:00:00 2001 From: Pika <15848969+ThatNerdyPikachu@users.noreply.github.com> Date: Mon, 24 Dec 2018 13:08:38 -0500 Subject: [PATCH 5/5] cnmt: fix compilation errors --- src/format/cnmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format/cnmt.rs b/src/format/cnmt.rs index 06ec63c..e78b2f0 100644 --- a/src/format/cnmt.rs +++ b/src/format/cnmt.rs @@ -75,7 +75,7 @@ impl Cnmt { file.seek(SeekFrom::Current(12))?; let title_header = match title_type { - TitleType::Application | TitleType::Patch | TitleType::AddOnContent => { + TitleType::Application | TitleType::Patch | TitleType::AddOnContent => Some(TitleHeader { title_id: file.read_u64::()?, minimum_version: file.read_u32::()?