Skip to content

Commit 9e0f50d

Browse files
committed
added no-verbose mode
1 parent 86348ff commit 9e0f50d

File tree

2 files changed

+65
-37
lines changed

2 files changed

+65
-37
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ sha256 = "1.4.0"
1414
attohttpc = "0.28.0"
1515
block-padding = "0.3.3"
1616
ctr = "0.9.2"
17-
byteorder = "1.5.0"
17+
byteorder = "1.5.0"
18+
log = "0.4.26"
19+
env_logger = "0.11.6"

src/main.rs

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use aes::{cipher::{KeyIvInit, StreamCipher}, Aes128};
77
use std::{collections::HashMap, env, fs::File, io::{Cursor, Read, Seek, SeekFrom, Write}, path::Path, usize, vec};
88

99
use hex_literal::hex;
10+
use log::{debug, info, LevelFilter};
1011

1112
const CMNKEYS: [[u8; 16]; 6] = [
1213
hex!("64c5fd55dd3ad988325baaec5243db98"),
@@ -99,9 +100,9 @@ fn scramblekey(key_x: u128, key_y: u128) -> u128 {
99100
fn dump_section(ncch: &mut File, cia: &mut CiaReader, offset: u64, size: u32, sec_type: NcchSection, sec_idx: usize, ctr: [u8; 16], uses_extra_crypto: u8, fixed_crypto: u8, encrypted: bool, keyys: [u128; 2]) {
100101
let sections = ["ExHeader", "ExeFS", "RomFS"];
101102
const CHUNK: u32 = 4194304; // 4 MiB
102-
println!(" {} offset: {:08X}", sections[sec_idx], offset);
103-
println!(" {} counter: {}", sections[sec_idx], hex::encode(&ctr));
104-
println!(" {} size: {} bytes", sections[sec_idx], size);
103+
debug!(" {} offset: {:08X}", sections[sec_idx], offset);
104+
debug!(" {} counter: {}", sections[sec_idx], hex::encode(&ctr));
105+
debug!(" {} size: {} bytes", sections[sec_idx], size);
105106

106107
// Prevent integer overflow
107108
match offset.checked_sub(ncch.stream_position().unwrap()) {
@@ -256,7 +257,7 @@ fn get_new_key(key_y: u128, header: &NcchHdr, titleid: String) -> u128 {
256257
seeddb.seek(SeekFrom::Current(8)).unwrap();
257258
}
258259
}
259-
Err(_) => println!("seeddb.bin not found, trying to connect to Nintendo servers...")
260+
Err(_) => debug!("seeddb.bin not found, trying to connect to Nintendo servers...")
260261
}
261262

262263
// Check into Nintendo's servers
@@ -272,7 +273,7 @@ fn get_new_key(key_y: u128, header: &NcchHdr, titleid: String) -> u128 {
272273
match bytes.try_into() {
273274
Ok(bytes) => {
274275
seeds.insert(titleid.clone(), bytes);
275-
println!("A seed has been found online in the region {}", country);
276+
debug!("A seed has been found online in the region {}", country);
276277
break;
277278
}
278279
Err(_) => ()
@@ -297,7 +298,7 @@ fn get_new_key(key_y: u128, header: &NcchHdr, titleid: String) -> u128 {
297298
}
298299

299300
fn parse_ncsd(cia: &mut CiaReader) {
300-
println!("Parsing NCSD in file: {}", cia.name);
301+
debug!("Parsing NCSD in file: {}", cia.name);
301302
cia.seek(0);
302303
let mut tmp: [u8; 512] = [0u8; 512];
303304
cia.read(&mut tmp);
@@ -314,11 +315,11 @@ fn parse_ncsd(cia: &mut CiaReader) {
314315

315316
fn parse_ncch(cia: &mut CiaReader, offs: u64, mut titleid: [u8; 8]) {
316317
if cia.from_ncsd {
317-
println!(" Parsing {} NCCH", NCSD_PARTITIONS[cia.cidx as usize]);
318+
debug!(" Parsing {} NCCH", NCSD_PARTITIONS[cia.cidx as usize]);
318319
} else if cia.single_ncch {
319-
println!(" Parsing NCCH in file: {}", cia.name);
320+
debug!(" Parsing NCCH in file: {}", cia.name);
320321
} else {
321-
println!("Parsing NCCH: {}", cia.cidx)
322+
debug!("Parsing NCCH: {}", cia.cidx)
322323
}
323324

324325
cia.seek(offs);
@@ -332,39 +333,39 @@ fn parse_ncch(cia: &mut CiaReader, offs: u64, mut titleid: [u8; 8]) {
332333

333334
let ncch_key_y = BigEndian::read_u128(header.signature[0..16].try_into().unwrap());
334335

335-
println!(" Product code: {}", std::str::from_utf8(&header.productcode).unwrap());
336-
println!(" KeyY: {:032X}", ncch_key_y);
336+
debug!(" Product code: {}", std::str::from_utf8(&header.productcode).unwrap());
337+
debug!(" KeyY: {:032X}", ncch_key_y);
337338
header.titleid.reverse();
338-
println!(" Title ID: {}", hex::encode(header.titleid).to_uppercase());
339+
debug!(" Title ID: {}", hex::encode(header.titleid).to_uppercase());
339340
header.titleid.reverse();
340-
println!(" Content ID: {:08X}\n", cia.content_id);
341-
println!(" Format version: {}\n", header.formatversion);
341+
debug!(" Content ID: {:08X}\n", cia.content_id);
342+
debug!(" Format version: {}\n", header.formatversion);
342343

343344
let uses_extra_crypto: u8 = header.flags[3];
344345

345346
if flag_to_bool(uses_extra_crypto) {
346-
println!(" Uses extra NCCH crypto, keyslot 0x25");
347+
debug!(" Uses extra NCCH crypto, keyslot 0x25");
347348
}
348349

349350
let mut fixed_crypto: u8 = 0;
350351
let mut encrypted: bool = true;
351352

352353
if flag_to_bool(header.flags[7] & 1) {
353354
if flag_to_bool(header.titleid[3] & 16) { fixed_crypto = 2 } else { fixed_crypto = 1 }
354-
println!(" Uses fixed-key crypto")
355+
debug!(" Uses fixed-key crypto")
355356
}
356357

357358
if flag_to_bool(header.flags[7] & 4) {
358359
encrypted = false;
359-
println!(" Not encrypted")
360+
debug!(" Not encrypted")
360361
}
361362

362363
let use_seed_crypto: bool = (header.flags[7] & 32) != 0;
363364
let mut key_y = ncch_key_y;
364365

365366
if use_seed_crypto {
366367
key_y = get_new_key(ncch_key_y, &header, hex::encode(titleid));
367-
println!("Uses 9.6 NCCH Seed crypto with KeyY: {:032X}", key_y);
368+
debug!("Uses 9.6 NCCH Seed crypto with KeyY: {:032X}", key_y);
368369
}
369370

370371
let mut base: String;
@@ -383,7 +384,7 @@ fn parse_ncch(cia: &mut CiaReader, offs: u64, mut titleid: [u8; 8]) {
383384
cia.content_id
384385
);
385386

386-
let mut ncch: File = File::create(base).unwrap();
387+
let mut ncch: File = File::create(base.clone()).unwrap();
387388
tmp[399] = tmp[399] & 2 | 4;
388389

389390
ncch.write_all(&tmp).unwrap();
@@ -402,6 +403,8 @@ fn parse_ncch(cia: &mut CiaReader, offs: u64, mut titleid: [u8; 8]) {
402403
counter = get_ncch_aes_counter(&header, NcchSection::RomFS);
403404
dump_section(&mut ncch, cia, (header.romfsoffset * MEDIA_UNIT_SIZE) as u64, header.romfssize * MEDIA_UNIT_SIZE, NcchSection::RomFS, 2, counter, uses_extra_crypto, fixed_crypto, encrypted, [ncch_key_y, key_y]);
404405
}
406+
407+
info!("{}", base);
405408
}
406409

407410
fn parse_cia(mut romfile: File, filename: String, partition: Option<u8>) {
@@ -423,7 +426,7 @@ fn parse_cia(mut romfile: File, filename: String, partition: Option<u8>) {
423426
romfile.read_exact(&mut tid[0..8]).unwrap();
424427

425428
if hex::encode(tid).starts_with("00048") {
426-
println!("Unsupported CIA file");
429+
debug!("Unsupported CIA file");
427430
return
428431
}
429432

@@ -479,35 +482,58 @@ fn parse_cia(mut romfile: File, filename: String, partition: Option<u8>) {
479482
}
480483
parse_ncch(&mut cia_handle, 0, tid[0..8].try_into().unwrap());
481484

482-
} else { println!("CIA content can't be parsed, skipping partition") }
483-
Err(_) => println!("CIA content can't be parsed, skipping partition")
485+
} else { debug!("CIA content can't be parsed, skipping partition") }
486+
Err(_) => debug!("CIA content can't be parsed, skipping partition")
484487
}
485488
}
486489
}
487490

488491
fn main() {
489492
let args: Vec<String> = std::env::args().collect();
493+
490494
let mut partition: Option<u8> = None;
491-
if args.len() < 2 || args.len() == 3 {
492-
// Decrypting a specific NCCH container is supported only for .cia backups
493-
println!("Usage: ctrdecrypt <ROMFILE> [--ncch <partion-number>]");
495+
let mut verbose = true;
496+
497+
if args.len() < 2 {
498+
println!("Usage: ctrdecrypt <ROMFILE> [OPTIONS]\nOptions:\n\t--ncch <partition-number>\n\t--no-verbose");
494499
return;
495-
} else if !Path::exists(Path::new(&args[1])) {
500+
}
501+
502+
if !Path::exists(Path::new(&args[1])) {
496503
println!("ROM does not exist");
497504
return;
498-
} else if args.len() == 4 {
499-
if args[2] != "--ncch" {
500-
println!("Invalid argument: {}", args[2]);
501-
return;
502-
}
503-
if args[3].parse::<u8>().is_err() {
504-
println!("Invalid partition number: {}", args[3]);
505-
return;
506-
} else {
507-
partition = Some(args[3].parse::<u8>().unwrap());
505+
}
506+
507+
let mut i = 2;
508+
while i < args.len() {
509+
match args[i].as_str() {
510+
"--ncch" => {
511+
if i + 1 >= args.len() {
512+
println!("Missing partition number");
513+
return;
514+
}
515+
match args[i + 1].parse::<u8>() {
516+
Ok(num) => partition = Some(num),
517+
Err(_) => {
518+
println!("Invalid partition number: {}", args[i + 1]);
519+
}
520+
}
521+
i += 1; // Partition number already checked
522+
}
523+
"--no-verbose" => verbose = false,
524+
_ => {
525+
println!("Invalid argument: {}", args[i]);
526+
return;
527+
}
508528
}
529+
i += 1;
509530
}
510531

532+
env_logger::Builder::new()
533+
.format(|buf, record| writeln!(buf, "{}", record.args()))
534+
.filter(None, if verbose { LevelFilter::Debug } else { LevelFilter::Info })
535+
.init();
536+
511537
let mut rom = File::open(&args[1]).unwrap();
512538
rom.seek(SeekFrom::Start(256)).unwrap();
513539
let mut magic: [u8; 4] = [0u8; 4];

0 commit comments

Comments
 (0)