mirror of
https://github.com/Zenithsiz/ndsz.git
synced 2026-02-03 09:50:35 +00:00
Removed --fat, --fnt and --fat-files arguments.
Hidden fat files are now always extracted.
This commit is contained in:
parent
a42583f607
commit
a7bb1a9373
@ -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,
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
@ -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),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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")?;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user