diff --git a/Cargo.lock b/Cargo.lock index ac34a57..2cbeb74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,6 +198,16 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "goblin" +version = "0.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.5" @@ -226,7 +236,9 @@ dependencies = [ "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)", "elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "lz4 1.22.0 (git+https://github.com/bozaro/lz4-rs.git)", + "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)", @@ -234,6 +246,14 @@ dependencies = [ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "log" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lz4" version = "1.22.0" @@ -263,6 +283,11 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.9" @@ -331,6 +356,24 @@ dependencies = [ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "scroll" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.9 (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)", +] + [[package]] name = "semver" version = "0.9.0" @@ -546,13 +589,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2" "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 libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" "checksum lz4 1.22.0 (git+https://github.com/bozaro/lz4-rs.git)" = "" "checksum lz4-sys 1.8.0 (git+https://github.com/bozaro/lz4-rs.git)" = "" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b71f9f575d55555aa9c06188be9d4e2bfc83ed02537948ac0d520c24d0419f1a" @@ -562,6 +608,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" +"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" diff --git a/Cargo.toml b/Cargo.toml index e6e0839..7b5ce13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,13 @@ byteorder = "1" lz4 = { git = "https://github.com/bozaro/lz4-rs.git"} clap = {version = "2", optional = true} sha2 = "0.7.1" +scroll = { version = "0.9.0", optional = true } serde = "1" serde_derive = "1" serde_json = "1" cargo_metadata = { git = "https://github.com/roblabla/cargo_metadata", optional = true } url = "1.7.1" +goblin = { version = "0.0.17", optional = true } [features] -binaries = ["clap", "cargo_metadata"] +binaries = ["clap", "cargo_metadata", "scroll", "goblin"] diff --git a/src/bin/cargo-nro.rs b/src/bin/cargo-nro.rs index fc09d1d..93bf1c6 100644 --- a/src/bin/cargo-nro.rs +++ b/src/bin/cargo-nro.rs @@ -7,15 +7,22 @@ extern crate serde_json; #[macro_use] extern crate serde_derive; extern crate cargo_metadata; +extern crate goblin; +extern crate scroll; use std::env::{self, VarError}; use std::process::{Command, Stdio}; use std::path::{Path, PathBuf}; use std::fs::File; -use linkle::format::{nxo::NxoFile, nacp::NacpFile}; +use std::io::{Write, Read}; +use scroll::IOwrite; + +use linkle::format::{romfs::RomFs, nxo::NxoFile, nacp::NacpFile}; use cargo_metadata::{Package, Message}; use clap::{Arg, App}; use url::Url; +use goblin::elf::{Elf, Header as ElfHeader, ProgramHeader}; +use goblin::elf::section_header::{SHT_NOBITS, SHT_SYMTAB, SHT_STRTAB}; fn find_project_root(path: &Path) -> Option<&Path> { for parent in path.ancestors() { @@ -69,6 +76,106 @@ fn get_metadata(manifest_path: &Path, package_id: &str, target_name: &str) -> (P (package, package_metadata) } +trait BetterIOWrite: IOwrite { + fn iowrite_with_try + scroll::ctx::TryIntoCtx>(&mut self, n: N, ctx: Ctx) + -> Result<(), N::Error> + where + N::Error: From + { + let mut buf = [0u8; 256]; + let size = N::size_with(&ctx); + let buf = &mut buf[0..size]; + n.try_into_ctx(buf, ctx)?; + self.write_all(buf)?; + Ok(()) + } +} + +impl + ?Sized> BetterIOWrite for W {} + +fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) -> goblin::error::Result { + let mut elf_file = File::open(elf_path)?; + let mut buffer = Vec::new(); + elf_file.read_to_end(&mut buffer)?; + let elf = goblin::elf::Elf::parse(&buffer)?; + let new_file = { + let mut new_path = PathBuf::from(elf_path); + new_path.set_extension("debug"); + let mut file = File::create(&new_path)?; + let Elf { + mut header, + program_headers, + mut section_headers, + is_64, + little_endian, + .. + } = elf; + + let ctx = goblin::container::Ctx { + container: if is_64 { goblin::container::Container::Big } else { goblin::container::Container::Little }, + le: if little_endian { goblin::container::Endian::Little } else { goblin::container::Endian::Big } + }; + + for section in section_headers.iter_mut() { + if section.sh_type == SHT_NOBITS || section.sh_type == SHT_SYMTAB || section.sh_type == SHT_STRTAB { + continue; + } + if let Some(Ok(s)) = elf.shdr_strtab.get(section.sh_name) { + if !(s.starts_with(".debug") || s == ".comment") { + section.sh_type = SHT_NOBITS; + } + } + } + + // Calculate section data length + elf/program headers + let data_off = ElfHeader::size(&ctx) + ProgramHeader::size(&ctx) * program_headers.len(); + let shoff = data_off as u64 + section_headers.iter().map(|v| { + if v.sh_type != SHT_NOBITS { + v.sh_size + } else { + 0 + } + }).sum::(); + + // Write ELF header + // TODO: Anything else? + header.e_phoff = ::std::mem::size_of::() as u64; + header.e_shoff = shoff; + file.iowrite_with(header, ctx)?; + + // Write program headers + for phdr in program_headers { + file.iowrite_with_try(phdr, ctx)?; + } + + // Write section data + let mut cur_idx = data_off; + for section in section_headers.iter_mut().filter(|v| v.sh_type != SHT_NOBITS) { + file.write_all(&buffer[section.sh_offset as usize..(section.sh_offset + section.sh_size) as usize])?; + section.sh_offset = cur_idx as u64; + cur_idx += section.sh_size as usize; + } + + // Write section headers + for section in section_headers { + file.iowrite_with(section, ctx)?; + } + + file.sync_all()?; + new_path + }; + + let mut romfs = if let Some(romfs) = romfs { + RomFs::from_directory(romfs.as_ref())? + } else { + RomFs::empty() + }; + + romfs.push_file(&new_file, String::from("debug_info.elf"))?; + + Ok(romfs) +} + #[derive(Debug, Serialize, Deserialize, Default)] struct PackageMetadata { romfs: Option, @@ -154,9 +261,6 @@ fn main() { None }; - let romfs = romfs.map(|v| v.to_string_lossy().into_owned()); - let romfs = romfs.as_ref().map(|v: &String| v.as_ref()); - let icon_file = if let Some(icon) = target_metadata.icon { let icon_path = root.join(icon); if !icon_path.is_file() { @@ -181,11 +285,14 @@ fn main() { nacp.title_id = target_metadata.title_id; } + let romfs = generate_debuginfo_romfs(Path::new(&artifact.filenames[0]), romfs).unwrap(); + let mut new_name = PathBuf::from(artifact.filenames[0].clone()); assert!(new_name.set_extension("nro")); + NxoFile::from_elf(&artifact.filenames[0]).unwrap() .write_nro(&mut File::create(new_name.clone()).unwrap(), - romfs, + Some(romfs), icon_file, Some(nacp) ).unwrap(); diff --git a/src/bin/linkle_clap.rs b/src/bin/linkle_clap.rs index a9925d3..562a3dc 100644 --- a/src/bin/linkle_clap.rs +++ b/src/bin/linkle_clap.rs @@ -6,13 +6,18 @@ extern crate linkle; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use std::fs::OpenOptions; +use std::path::Path; use std::process; fn create_nxo(format: &str, matches: &ArgMatches) -> std::io::Result<()> { let input_file = matches.value_of("INPUT_FILE").unwrap(); let output_file = matches.value_of("OUTPUT_FILE").unwrap(); - let romfs_dir = matches.value_of("ROMFS_PATH"); let icon_file = matches.value_of("ICON_PATH"); + let romfs_dir = if let Some(romfs_path) = matches.value_of_os("ROMFS_PATH") { + Some(linkle::format::romfs::RomFs::from_directory(Path::new(romfs_path))?) + } else { + None + }; let nacp_file = if let Some(nacp_path) = matches.value_of("NACP_FILE") { Some(linkle::format::nacp::NacpFile::from_file(nacp_path)?) } else { @@ -50,9 +55,9 @@ fn create_nacp(matches: &ArgMatches) -> std::io::Result<()> { } fn create_romfs(matches: &ArgMatches) -> std::io::Result<()> { - let input_directory = matches.value_of("INPUT_DIRECTORY").unwrap(); + let input_directory = matches.value_of_os("INPUT_DIRECTORY").unwrap(); let output_file = matches.value_of("OUTPUT_FILE").unwrap(); - let romfs = linkle::format::romfs::RomFs::from_directory(input_directory)?; + let romfs = linkle::format::romfs::RomFs::from_directory(Path::new(input_directory))?; let mut option = OpenOptions::new(); let output_option = option.write(true).create(true).truncate(true); romfs.write(&mut output_option.open(output_file)?)?; diff --git a/src/format/nacp.rs b/src/format/nacp.rs index 88dc14a..9cf8b02 100644 --- a/src/format/nacp.rs +++ b/src/format/nacp.rs @@ -125,7 +125,7 @@ impl NacpFile { let default_lang_entry = NacpLangEntry { name, author }; match lang_entries { None => { - for _ in 0..15 { + for _ in 0..16 { self.write_lang_entry(output_writter, &default_lang_entry)?; } } @@ -234,10 +234,12 @@ impl NacpFile { self.write_lang_entry( output_writter, &lang_entries - .zh_tw + .zh_cn .clone() .unwrap_or(default_lang_entry.clone()), )?; + // There are 16 entries. One is missing :eyes: + self.write_lang_entry(output_writter, &default_lang_entry)?; } } @@ -279,3 +281,15 @@ impl NacpFile { Ok(()) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn nacp_is_4000_size() { + let mut buf = Vec::new(); + NacpFile::default().write(&mut buf).unwrap(); + assert_eq!(buf.len(), 0x4000, "Nacp length is wrong"); + } +} diff --git a/src/format/nxo.rs b/src/format/nxo.rs index 8997aa1..172277e 100644 --- a/src/format/nxo.rs +++ b/src/format/nxo.rs @@ -120,7 +120,7 @@ impl NxoFile { }) } - pub fn write_nro(&mut self, output_writter: &mut T, romfs: Option<&str>, icon: Option<&str>, nacp: Option) -> std::io::Result<()> + pub fn write_nro(&mut self, output_writter: &mut T, romfs: Option, icon: Option<&str>, nacp: Option) -> std::io::Result<()> where T: Write, { @@ -234,7 +234,7 @@ impl NxoFile { output_writter.write_u32::(0)?; // version // Offset to the next available region. - let mut offset = total_len as u64 + 8 + 16 + 16 + 16; + let mut offset = 8 + 16 + 16 + 16; let icon_len = if let Some(icon) = &icon { // TODO: Check if icon is a 256x256 JPEG. Convert it if it isn't? @@ -263,15 +263,12 @@ impl NxoFile { offset += nacp_len; - let romfs = if let Some(romfs) = &romfs { - let romfs = RomFs::from_directory(romfs)?; + if let Some(romfs) = &romfs { output_writter.write_u64::(offset)?; output_writter.write_u64::(romfs.len() as u64)?; - Some(romfs) } else { output_writter.write_u64::(0)?; output_writter.write_u64::(0)?; - None }; if let Some(icon) = icon { diff --git a/src/format/romfs.rs b/src/format/romfs.rs index 9cf394d..ab4fa57 100644 --- a/src/format/romfs.rs +++ b/src/format/romfs.rs @@ -3,11 +3,12 @@ use std::rc::{Rc, Weak}; use std::io::{self, Write, Cursor}; use std::fs::{self, File}; use std::cell::RefCell; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use byteorder::{WriteBytesExt, LE}; #[derive(Debug)] struct RomFsDirEntCtx { + // Only used in `RomFs::from_directory` system_path: PathBuf, name: String, entry_offset: u32, @@ -16,6 +17,19 @@ struct RomFsDirEntCtx { file: Vec>>, } +impl RomFsDirEntCtx { + fn internal_path(&self) -> String { + let mut path = self.name.clone(); + let mut cur = self.parent.upgrade().unwrap(); + while cur.borrow().name != "" { + path = cur.borrow().name.clone() + "/" + &path; + let new_cur = cur.borrow().parent.upgrade().unwrap(); + cur = new_cur; + } + path + } +} + #[derive(Debug)] struct RomFsFileEntCtx { system_path: PathBuf, @@ -26,6 +40,14 @@ struct RomFsFileEntCtx { parent: Weak>, } +impl RomFsFileEntCtx { + fn internal_path(&self) -> String { + let parent = self.parent.upgrade().unwrap(); + let parent_borrow = parent.borrow(); + parent_borrow.internal_path() + "/" + &self.name + } +} + #[repr(C)] #[derive(Debug)] struct RomFsDirEntryHdr { @@ -59,6 +81,23 @@ impl RomFsDirEntCtx { file: vec![] })) } + + fn new_root() -> Rc> { + // We'll want the parent to point to itself, so let's first + // set it to an unbound Weak, and set it to itself + // after creation. + let root = Rc::new(RefCell::new(RomFsDirEntCtx { + system_path: PathBuf::from(""), + name: String::from(""), + entry_offset: 0, + parent: Weak::new(), + child: vec![], + file: vec![] + })); + let weak = Rc::downgrade(&root); + root.borrow_mut().parent = weak; + root + } } // From https://www.3dbrew.org/wiki/RomFS @@ -116,30 +155,69 @@ pub struct RomFs { } impl RomFs { - pub fn from_directory(path: &str) -> io::Result { - // Stack of directories to visit. We'll iterate over it. When finding - // new directories, we'll push them to this stack, so that iteration may - // continue. This avoids doing recursive functions (which runs the risk - // of stack overflowing). - let mut dirs = vec![]; - - // First, let's create our root folder. We'll want the parent to be - // itself, so let's first set it to an unbound Weak, and set it to itself - // after creation. - let root_folder = RomFsDirEntCtx::new(Weak::new(), PathBuf::from(path)); - { - let mut root_folder_borrow = root_folder.borrow_mut(); - - root_folder_borrow.parent = Rc::downgrade(&root_folder.clone()); - // The name is empty for the root dir. - root_folder_borrow.name = String::from(""); + // Internal path + pub fn push_file(&mut self, file_path: &Path, internal_path: String) -> io::Result<()> { + let mut parent = self.dirs[0].clone(); + + let mut components = internal_path.split("/").peekable(); + while let Some(component) = components.next() { + if components.peek().is_none() { + let metadata = file_path.metadata()?; + // Handling last component. Add the file. + let file_to_add = Rc::new(RefCell::new(RomFsFileEntCtx { + system_path: PathBuf::from(file_path), + name: String::from(component), + entry_offset: 0, + offset: 0, + size: metadata.len(), + parent: Rc::downgrade(&parent), + })); + self.files.push(file_to_add.clone()); + parent.borrow_mut().file.push(file_to_add.clone()); + parent.borrow_mut().file.sort_by_key(|v| v.borrow().name.clone()); + + self.file_table_size += mem::size_of::() as u64 + align64(file_to_add.borrow().name.len() as u64, 4); + } else { + // Handling a parent component. Find the directory, create if it doesn't exist. + if component == "" { + continue; + } + let new_parent = if let Some(child) = parent.borrow().child.iter().find(|v| v.borrow().name == component) { + child.clone() + } else { + // system_path is not used outside from_directory. It's okay if it doesn't + // point to something "safe" (or to anything at all) + let child = Rc::new(RefCell::new(RomFsDirEntCtx { + system_path: PathBuf::from(""), + name: String::from(component), + entry_offset: 0, + parent: Rc::downgrade(&parent), + child: vec![], + file: vec![] + })); + self.dirs.push(child.clone()); + parent.borrow_mut().child.push(child.clone()); + parent.borrow_mut().child.sort_by_key(|v| v.borrow().name.clone()); + + self.dir_table_size += mem::size_of::() as u64 + align64(child.borrow().name.len() as u64, 4); + child + }; + parent = new_parent; + } } + self.files.sort_by_key(|v| v.borrow().internal_path()); + self.dirs.sort_by_key(|v| v.borrow().internal_path()); + self.calculate_offsets(); + Ok(()) + } - // Let's build our context. This will be returned. It contains the graph - // of files/directories, and some meta-information that will be used to - // write the romfs file afterwards. + pub fn empty() -> RomFs { + // First, let's create our root folder. + let root_folder = RomFsDirEntCtx::new_root(); + + // And now, build the empty context, with just the root directory in it. let mut ctx = RomFs { - dirs: vec![], + dirs: vec![root_folder], files: vec![], // We have the root dir already. dir_table_size: mem::size_of::() as u64, // Root Dir @@ -147,15 +225,32 @@ impl RomFs { file_partition_size: 0, }; - // Let's start iterating. - ctx.dirs.push(root_folder.clone()); - dirs.push(root_folder); + ctx.calculate_offsets(); + + ctx + } + + pub fn from_directory(path: &Path) -> io::Result { + // Stack of directories to visit. We'll iterate over it. When finding + // new directories, we'll push them to this stack, so that iteration may + // continue. This avoids doing recursive functions (which runs the risk + // of stack overflowing). + let mut dirs = vec![]; + + // Let's build our context. This will be returned. It contains the graph + // of files/directories, and some meta-information that will be used to + // write the romfs file afterwards. + let mut ctx = RomFs::empty(); + + // Set the system_path of the root directory. + ctx.dirs[0].borrow_mut().system_path = PathBuf::from(path); + + // Let's start iterating with the root directory. + dirs.push(ctx.dirs[0].clone()); while let Some(parent_dir) = dirs.pop() { let path = parent_dir.borrow().system_path.clone(); - let cur_dir_idx = ctx.dirs.len(); - for entry in fs::read_dir(path)? { let entry = entry?; let file_type = entry.file_type()?; @@ -198,22 +293,37 @@ impl RomFs { } parent_dir.borrow_mut().child.sort_by_key(|v| v.borrow().name.clone()); parent_dir.borrow_mut().file.sort_by_key(|v| v.borrow().name.clone()); - ctx.dirs[cur_dir_idx..].sort_by_key(|v| v.borrow().name.clone()); } - ctx.files.sort_by_key(|v| v.borrow().system_path.to_string_lossy().into_owned()); + ctx.files.sort_by_key(|v| v.borrow().internal_path()); + ctx.dirs.sort_by_key(|v| v.borrow().internal_path()); + + ctx.calculate_offsets(); + + Ok(ctx) + } + + pub fn len(&self) -> usize { + (align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4) + + romfs_get_hash_table_count(self.dirs.len() * mem::size_of::()) as u64 + + self.dir_table_size + + romfs_get_hash_table_count(self.files.len() * mem::size_of::()) as u64 + + self.file_table_size) as usize + } + fn calculate_offsets(&mut self) { // Calculate file offset and file partition size. let mut entry_offset = 0; - for file in ctx.files.iter_mut() { + self.file_partition_size = 0; + for file in self.files.iter_mut() { // Files have to start aligned at 0x10. We do this at the start to // avoid useless padding after the last file. - ctx.file_partition_size = align64(ctx.file_partition_size, 0x10); + self.file_partition_size = align64(self.file_partition_size, 0x10); // Update the data section size and set the file offset in the data // section. - file.borrow_mut().offset = ctx.file_partition_size; - ctx.file_partition_size += file.borrow().size; + file.borrow_mut().offset = self.file_partition_size; + self.file_partition_size += file.borrow().size; // Set the file offset in the file table section. file.borrow_mut().entry_offset = entry_offset; @@ -222,21 +332,12 @@ impl RomFs { // Calculate directory offsets. let mut entry_offset = 0; - for dir in ctx.dirs.iter_mut() { + for dir in self.dirs.iter_mut() { dir.borrow_mut().entry_offset = entry_offset; entry_offset += mem::size_of::() as u32 + align32(dir.borrow().name.len() as u32, 4); } - - Ok(ctx) } - pub fn len(&self) -> usize { - (align64(ROMFS_FILEPARTITION_OFS + self.file_partition_size, 4) + - romfs_get_hash_table_count(self.dirs.len() * mem::size_of::()) as u64 + - self.dir_table_size + - romfs_get_hash_table_count(self.files.len() * mem::size_of::()) as u64 + - self.file_table_size) as usize - } pub fn write(&self, to: &mut Write) -> io::Result<()> { const ROMFS_ENTRY_EMPTY: u32 = 0xFFFFFFFF; @@ -274,7 +375,7 @@ impl RomFs { let dir = dir.borrow(); let parent = dir.parent.upgrade().unwrap(); let parent = parent.borrow(); - let sibling = parent.child.windows(2).find(|window| window[0].borrow().system_path == dir.system_path).map(|window| window[1].borrow().entry_offset); + let sibling = parent.child.windows(2).find(|window| window[0].borrow().internal_path() == dir.internal_path()).map(|window| window[1].borrow().entry_offset); let hash = calc_path_hash(parent.entry_offset, &dir.name); let mut cursor = Cursor::new(&mut dir_table[dir.entry_offset as usize..]);