Removed --fat, --fnt and --fat-files arguments.

Hidden fat files are now always extracted.
This commit is contained in:
Filipe Rodrigues 2022-10-14 19:45:06 +01:00
parent a42583f607
commit a7bb1a9373
5 changed files with 71 additions and 198 deletions

View File

@ -15,22 +15,4 @@ pub struct Args {
/// Defaults to `input_path` without an extension
#[clap(long = "output", short = 'o')]
pub output_path: Option<PathBuf>,
/// Fat output
///
/// Outputs the fat as a yaml file for debugging
#[clap(long = "fat")]
pub fat: bool,
/// Fnt output
///
/// Outputs the fnt as a yaml file for debugging
#[clap(long = "fnt")]
pub fnt: bool,
/// Extract fat files
///
/// Extracts all fat files independently of the fnt
#[clap(long = "fat-files")]
pub fat_files: bool,
}

View File

@ -1,47 +0,0 @@
//! Fat debugging
// Imports
use {
anyhow::Context,
ndsz_fat::FileAllocationTable,
std::{collections::BTreeMap, fs, path::PathBuf},
};
/// Outputs `fat` as yaml to `path`
pub fn output_fat_yaml(fat: &FileAllocationTable, path: PathBuf) -> Result<(), anyhow::Error> {
let fat = FatYaml {
files: fat
.ptrs
.iter()
.map(|ptr| FatFileYaml {
start: ptr.start_address,
end: ptr.end_address,
})
.enumerate()
.collect(),
};
let output = fs::File::create(&path).with_context(|| format!("Unable to create file {path:?}"))?;
serde_yaml::to_writer(output, &fat).context("Unable to serialize fat")?;
Ok(())
}
/// Fat
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
struct FatYaml {
/// All files, by inode
pub files: BTreeMap<usize, FatFileYaml>,
}
/// Fat file
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
struct FatFileYaml {
/// Start address
pub start: u32,
/// End address
pub end: u32,
}

View File

@ -1,93 +0,0 @@
//! Fnt debugging
// Imports
use {
anyhow::Context,
ndsz_fat::{Dir, DirEntry, DirEntryKind, FileNameTable},
ndsz_util::AsciiStrArr,
std::{fs, path::PathBuf},
};
/// Outputs `fnt` as yaml to `path`
pub fn output_fnt_yaml(fnt: &FileNameTable, path: PathBuf) -> Result<(), anyhow::Error> {
let fnt = FntYaml::new(fnt);
let output = fs::File::create(&path).with_context(|| format!("Unable to create file {path:?}"))?;
serde_yaml::to_writer(output, &fnt).context("Unable to serialize fnt")?;
Ok(())
}
/// Fnt
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
struct FntYaml {
/// Root directory
pub root: FntDirYaml,
}
impl FntYaml {
fn new(fnt: &FileNameTable) -> Self {
Self {
root: FntDirYaml::new(&fnt.root),
}
}
}
/// Fnt directory
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
struct FntDirYaml {
/// Entries
pub entries: Vec<FntDirEntryYaml>,
}
impl FntDirYaml {
pub fn new(dir: &Dir) -> Self {
Self {
entries: dir.entries.iter().map(FntDirEntryYaml::new).collect(),
}
}
}
/// Fnt directory entry
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
struct FntDirEntryYaml {
/// Name
pub name: AsciiStrArr<0x80>,
/// Kind
#[serde(flatten)]
pub kind: FntDirEntryKindYaml,
}
impl FntDirEntryYaml {
fn new(entry: &DirEntry) -> Self {
Self {
name: entry.name,
kind: FntDirEntryKindYaml::new(&entry.kind),
}
}
}
/// Fnt directory entry kind
#[derive(Clone, Debug)]
#[derive(serde::Serialize)]
#[serde(untagged)]
enum FntDirEntryKindYaml {
File { id: u16 },
Dir { id: u16, dir: FntDirYaml },
}
impl FntDirEntryKindYaml {
pub fn new(kind: &DirEntryKind) -> Self {
match *kind {
DirEntryKind::File { id } => Self::File { id },
DirEntryKind::Dir { id, ref dir } => Self::Dir {
id,
dir: FntDirYaml::new(dir),
},
}
}
}

View File

@ -3,17 +3,18 @@
// Imports
use {
anyhow::Context,
ndsz_fat::{dir, Dir, FileAllocationTable},
ndsz_fat::{dir, Dir, FileAllocationTable, FileNameTable},
ndsz_util::{AsciiStrArr, IoSlice},
std::{
collections::HashSet,
fs,
io,
path::{Path, PathBuf},
},
};
/// Directory visitor
struct DirVisitor<'fat, 'reader, R> {
/// Fnt extraction visitor
struct ExtractorVisitor<'fat, 'reader, R> {
/// Current path
cur_path: PathBuf,
@ -27,11 +28,11 @@ struct DirVisitor<'fat, 'reader, R> {
fat: &'fat FileAllocationTable,
}
impl<'fat, 'reader, R: io::Read + io::Seek> dir::Visitor for DirVisitor<'fat, 'reader, R> {
impl<'fat, 'reader, R: io::Read + io::Seek> dir::Visitor for ExtractorVisitor<'fat, 'reader, R> {
type Error = anyhow::Error;
type SubDirVisitor<'visitor, 'entry> = DirVisitor<'fat, 'visitor, R>
where
Self: 'visitor;
type SubDirVisitor<'visitor, 'entry> = ExtractorVisitor<'fat, 'visitor, R>
where
Self: 'visitor;
fn visit_file(&mut self, name: &AsciiStrArr<0x80>, id: u16) -> Result<(), Self::Error> {
let path = self.cur_path.join(name.as_str());
@ -63,7 +64,7 @@ impl<'fat, 'reader, R: io::Read + io::Seek> dir::Visitor for DirVisitor<'fat, 'r
// Create the directory
fs::create_dir_all(&path).context("Unable to create directory")?;
Ok(DirVisitor {
Ok(ExtractorVisitor {
cur_path: path,
reader: self.reader,
fat: self.fat,
@ -71,6 +72,39 @@ impl<'fat, 'reader, R: io::Read + io::Seek> dir::Visitor for DirVisitor<'fat, 'r
}
}
/// Visitor that collects all files
struct CollectFilesVisitor<'fat, 'files> {
/// The fat
fat: &'fat FileAllocationTable,
/// All file ids visited
files_visited: &'files mut HashSet<u16>,
}
impl<'fat, 'files> dir::Visitor for CollectFilesVisitor<'fat, 'files> {
type Error = !;
type SubDirVisitor<'visitor, 'entry> = CollectFilesVisitor<'fat, 'visitor>
where
Self: 'visitor;
fn visit_file(&mut self, _name: &AsciiStrArr<0x80>, id: u16) -> Result<(), Self::Error> {
self.files_visited.insert(id);
Ok(())
}
fn visit_dir<'visitor, 'entry>(
&'visitor mut self,
_name: &'entry AsciiStrArr<0x80>,
_id: u16,
) -> Result<Self::SubDirVisitor<'visitor, 'entry>, Self::Error> {
Ok(CollectFilesVisitor {
fat: self.fat,
files_visited: self.files_visited,
})
}
}
/// Extracts all files from a fat directory
pub fn extract_fat_dir<R: io::Read + io::Seek>(
dir: &Dir,
@ -78,7 +112,7 @@ pub fn extract_fat_dir<R: io::Read + io::Seek>(
fat: &FileAllocationTable,
path: PathBuf,
) -> Result<(), anyhow::Error> {
let mut visitor = DirVisitor {
let mut visitor = ExtractorVisitor {
fat,
reader,
cur_path: path,
@ -86,15 +120,31 @@ pub fn extract_fat_dir<R: io::Read + io::Seek>(
dir.walk(&mut visitor).context("Unable to extract root directory")
}
/// Extracts the fat without the fnt
pub fn extract_fat_raw<R: io::Read + io::Seek>(
/// Extracts the hidden fat files not mentioned in the fnt
pub fn extract_fat_hidden<R: io::Read + io::Seek>(
fat: &FileAllocationTable,
fnt: &FileNameTable,
rom_file: &mut R,
output_path: &Path,
) -> Result<(), anyhow::Error> {
) -> Result<Vec<u16>, anyhow::Error> {
let mut fnt_files = HashSet::new();
fnt.root
.walk(&mut CollectFilesVisitor {
fat,
files_visited: &mut fnt_files,
})
.into_ok();
let mut extracted = vec![];
let fat_dir = output_path.join("fat");
fs::create_dir_all(&fat_dir).context("Unable to create fat output directory")?;
for (idx, ptr) in fat.ptrs.iter().enumerate() {
for (ptr, idx) in fat.ptrs.iter().zip(0..) {
// If this file isn't hidden, continue
if fnt_files.contains(&idx) {
continue;
}
extracted.push(idx);
let name = format!("{idx}.bin");
self::extract_part(
rom_file,
@ -106,7 +156,7 @@ pub fn extract_fat_raw<R: io::Read + io::Seek>(
.with_context(|| format!("Unable to extract fat entry #{idx} ({ptr:?})"))?;
}
Ok(())
Ok(extracted)
}
/// Extract all parts of the nds header, except the filesystem

View File

@ -1,21 +1,18 @@
//! Unpacks a `.nds`
// Features
#![feature(fs_try_exists)]
#![feature(fs_try_exists, never_type, unwrap_infallible)]
// Modules
mod args;
mod debug_fat;
mod debug_fnt;
mod extract;
// Imports
use {
self::{
args::Args,
debug_fat::output_fat_yaml,
debug_fnt::output_fnt_yaml,
extract::{extract_all_parts, extract_fat_dir, extract_fat_raw},
extract::{extract_all_parts, extract_fat_dir, extract_fat_hidden},
yaml::output_yaml,
},
anyhow::Context,
clap::Parser,
@ -45,33 +42,17 @@ fn main() -> Result<(), anyhow::Error> {
let mut input_file = fs::File::open(&args.input_path).context("Unable to open input file")?;
// Read the header
let header = parse_header(&mut input_file)?;
let header = self::parse_header(&mut input_file)?;
tracing::trace!(?header);
// Get the fat, then output it, and it's files, if requested
// Parses the fat and fnt
let fat = self::parse_fat(&mut input_file, &header)?;
if args.fat {
let fat_path = output_path.join("fat.yaml");
self::output_fat_yaml(&fat, fat_path).context("Unable to output fat yaml")?;
}
if args.fat_files {
self::extract_fat_raw(&fat, &mut input_file, &output_path)?;
}
// Get the fnt, then output it, if requested
let fnt = self::parse_fnt(&mut input_file, &header)?;
if args.fnt {
let fnt_path = output_path.join("fnt.yaml");
self::output_fnt_yaml(&fnt, fnt_path).context("Unable to output fnt yaml")?;
}
// Then extract all parts, and the directory structure
// Then extract all parts, as well as files not mentioned in the fnt
fs::create_dir_all(&output_path).context("Unable to create output directory")?;
self::extract_all_parts(&mut input_file, &header, &output_path).context("Unable to extract parts")?;
let hidden_fat_files = self::extract_fat_hidden(&fat, &fnt, &mut input_file, &output_path)?;
let fs_dir = output_path.join("fs");
self::extract_fat_dir(&fnt.root, &mut input_file, &fat, fs_dir).context("Unable to extract fat")?;