|
|
@ -60,6 +60,11 @@ pub struct File { |
|
|
|
offset: u64, |
|
|
|
offset: u64, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Metadata { |
|
|
|
|
|
|
|
attributes: u32, |
|
|
|
|
|
|
|
size: u64, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)] |
|
|
|
#[derive(Clone)] |
|
|
|
pub struct OpenOptions { |
|
|
|
pub struct OpenOptions { |
|
|
|
read: bool, |
|
|
|
read: bool, |
|
|
@ -74,8 +79,8 @@ pub struct ReadDir { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub struct DirEntry { |
|
|
|
pub struct DirEntry { |
|
|
|
root: Arc<PathBuf>, |
|
|
|
|
|
|
|
entry: FS_DirectoryEntry, |
|
|
|
entry: FS_DirectoryEntry, |
|
|
|
|
|
|
|
root: Arc<PathBuf>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct Dir(u32); |
|
|
|
struct Dir(u32); |
|
|
@ -128,25 +133,27 @@ impl File { |
|
|
|
OpenOptions::new().write(true).create(true).archive(arch).open(path.as_ref()) |
|
|
|
OpenOptions::new().write(true).create(true).archive(arch).open(path.as_ref()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> Result<u64, i32> { |
|
|
|
pub fn set_len(&mut self, len: u64) -> Result<(), i32> { |
|
|
|
unsafe { |
|
|
|
unsafe { |
|
|
|
let mut len = 0; |
|
|
|
let r = FSFILE_SetSize(self.handle, len); |
|
|
|
let r = FSFILE_GetSize(self.handle, &mut len); |
|
|
|
|
|
|
|
if r < 0 { |
|
|
|
if r < 0 { |
|
|
|
Err(r) |
|
|
|
Err(r) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Ok(len) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn set_len(&mut self, len: u64) -> Result<(), i32> { |
|
|
|
// Right now the only file metadata we really have is file size
|
|
|
|
|
|
|
|
// This will probably expand later on
|
|
|
|
|
|
|
|
pub fn metadata(&self) -> Result<Metadata, i32> { |
|
|
|
unsafe { |
|
|
|
unsafe { |
|
|
|
let r = FSFILE_SetSize(self.handle, len); |
|
|
|
let mut size = 0; |
|
|
|
|
|
|
|
let r = FSFILE_GetSize(self.handle, &mut size); |
|
|
|
if r < 0 { |
|
|
|
if r < 0 { |
|
|
|
Err(r) |
|
|
|
Err(r) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Ok(()) |
|
|
|
Ok(Metadata { attributes: 0, size: size }) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -197,6 +204,20 @@ impl File { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Metadata { |
|
|
|
|
|
|
|
pub fn is_dir(&self) -> bool { |
|
|
|
|
|
|
|
self.attributes == self.attributes | FS_ATTRIBUTE_DIRECTORY |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn is_file(&self) -> bool { |
|
|
|
|
|
|
|
!self.is_dir() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> u64 { |
|
|
|
|
|
|
|
self.size |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl OpenOptions { |
|
|
|
impl OpenOptions { |
|
|
|
pub fn new() -> OpenOptions { |
|
|
|
pub fn new() -> OpenOptions { |
|
|
|
OpenOptions { |
|
|
|
OpenOptions { |
|
|
@ -290,6 +311,12 @@ impl DirEntry { |
|
|
|
self.root.join(&self.file_name()) |
|
|
|
self.root.join(&self.file_name()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Requiring the user to explicitly pass in the Archive here is pretty ugly,
|
|
|
|
|
|
|
|
// But I'm not sure of how else to do it right now.
|
|
|
|
|
|
|
|
pub fn metadata(&self, arch: &Archive) -> Result<Metadata, i32> { |
|
|
|
|
|
|
|
metadata(&arch, self.path()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn file_name(&self) -> OsString { |
|
|
|
pub fn file_name(&self) -> OsString { |
|
|
|
let filename = truncate_utf16_at_nul(&self.entry.name); |
|
|
|
let filename = truncate_utf16_at_nul(&self.entry.name); |
|
|
|
OsString::from_wide(filename) |
|
|
|
OsString::from_wide(filename) |
|
|
@ -309,6 +336,16 @@ pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn metadata<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<Metadata, i32> { |
|
|
|
|
|
|
|
let maybe_file = File::open(&arch, path.as_ref()); |
|
|
|
|
|
|
|
let maybe_dir = read_dir(&arch, path.as_ref()); |
|
|
|
|
|
|
|
match (maybe_file, maybe_dir) { |
|
|
|
|
|
|
|
(Ok(file), _) => file.metadata(), |
|
|
|
|
|
|
|
(_, Ok(_dir)) => Ok(Metadata { attributes: FS_ATTRIBUTE_DIRECTORY, size: 0 }), |
|
|
|
|
|
|
|
(Err(r), _) => Err(r), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { |
|
|
|
pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> { |
|
|
|
unsafe { |
|
|
|
unsafe { |
|
|
|
let path = to_utf16(path.as_ref()); |
|
|
|
let path = to_utf16(path.as_ref()); |
|
|
@ -390,7 +427,7 @@ fn readdir(arch: &Archive, p: &Path) -> Result<ReadDir, i32> { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Determine if interior NULLs are premitted in 3DS file paths
|
|
|
|
// TODO: Determine if we should check UTF-16 paths for interior NULs
|
|
|
|
fn to_utf16(path: &Path) -> Vec<u16> { |
|
|
|
fn to_utf16(path: &Path) -> Vec<u16> { |
|
|
|
path.as_os_str().encode_wide().collect::<Vec<_>>() |
|
|
|
path.as_os_str().encode_wide().collect::<Vec<_>>() |
|
|
|
} |
|
|
|
} |
|
|
|