Browse Source

Merge pull request #10 from FenrirWolf/filesystem

Initial fs service support
pull/10/head
Ronald Kinard 8 years ago committed by GitHub
parent
commit
1876119809
  1. 24
      3ds.json
  2. 3
      Cargo.toml
  3. 4
      README.md
  4. 401
      ctru-sys/src/services/fs.rs
  5. 554
      src/ascii.rs
  6. 3
      src/ffi/mod.rs
  7. 576
      src/ffi/os_str.rs
  8. 24
      src/lib.rs
  9. 3378
      src/path.rs
  10. 20
      src/services/apt.rs
  11. 892
      src/services/fs.rs
  12. 1
      src/services/mod.rs
  13. 25
      src/sys/mod.rs
  14. 1206
      src/sys/wtf8.rs

24
3ds.json

@ -1,30 +1,32 @@ @@ -1,30 +1,32 @@
{
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"llvm-target": "arm-none-eabihf",
"linker": "arm-none-eabi-g++",
"linker": "arm-none-eabi-gcc",
"ar": "arm-none-eabi-ar",
"target-endian": "little",
"target-pointer-width": "32",
"arch": "arm",
"os": "none",
"os": "linux",
"cpu": "mpcore",
"features": "+vfp2",
"relocation-model": "static",
"linker-is-gnu": true,
"has-rpath": true,
"morestack": false,
"disable-redzone": true,
"executables": true,
"dynamic-linking": false,
"no-compiler-rt": true,
"exe-suffix": ".elf",
"is-like-windows": true,
"function-sections": false,
"pre-link-args": [
"-specs",
"3dsx.specs",
"-specs=3dsx.specs",
"-march=armv6k",
"-mtune=mpcore",
"-mfloat-abi=hard"
"-mfloat-abi=hard",
"-mtp=soft"
],
"post-link-args": [
"-lc",
"-lm",
"-lsysbase",
"-lc",
"-lgcc",
"-lc"
]
}

3
Cargo.toml

@ -10,6 +10,9 @@ version = "0.4.0" @@ -10,6 +10,9 @@ version = "0.4.0"
[dependencies.ctru-sys]
path = "ctru-sys"
[dependencies.alloc_system3ds]
git = "https://github.com/rust3ds/alloc_system3ds"
[lib]
crate-type = ["rlib"]
name = "ctru"

4
README.md

@ -60,3 +60,7 @@ applies to every file in the tree, unless otherwise noted. @@ -60,3 +60,7 @@ applies to every file in the tree, unless otherwise noted.
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Rust is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.
See [LICENSE-APACHE](https://github.com/rust-lang/rust/blob/master/LICENSE-APACHE), [LICENSE-MIT](https://github.com/rust-lang/rust/blob/master/LICENSE-MIT), and [COPYRIGHT](https://github.com/rust-lang/rust/blob/master/COPYRIGHT) for details.

401
ctru-sys/src/services/fs.rs

@ -1,44 +1,45 @@ @@ -1,44 +1,45 @@
// TODO: Determine if anonymous enums are properly represented (they probably aren't)
/* automatically generated by rust-bindgen */
#![allow(dead_code,
non_camel_case_types,
non_upper_case_globals,
non_snake_case)]
use ::{Handle, Result};
use ::libc::c_void;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed1 {
FS_OPEN_READ = 1,
FS_OPEN_WRITE = 2,
FS_OPEN_CREATE = 4,
}
pub const FS_OPEN_READ: u32 = 1;
pub const FS_OPEN_WRITE: u32 = 2;
pub const FS_OPEN_CREATE: u32 = 4;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed2 {
FS_WRITE_FLUSH = 1,
FS_WRITE_UPDATE_TIME = 256,
}
pub const FS_WRITE_FLUSH: u32 = 1;
pub const FS_WRITE_UPDATE_TIME: u32 = 256;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed3 {
FS_ATTRIBUTE_DIRECTORY = 1,
FS_ATTRIBUTE_HIDDEN = 256,
FS_ATTRIBUTE_ARCHIVE = 65536,
FS_ATTRIBUTE_READ_ONLY = 16777216,
}
pub const FS_ATTRIBUTE_DIRECTORY: u32 = 1;
pub const FS_ATTRIBUTE_HIDDEN: u32 = 256;
pub const FS_ATTRIBUTE_ARCHIVE: u32 = 65536;
pub const FS_ATTRIBUTE_READ_ONLY: u32 = 16777216;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed4 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_MediaType {
MEDIATYPE_NAND = 0,
MEDIATYPE_SD = 1,
MEDIATYPE_GAME_CARD = 2,
}
pub type FS_MediaType = Enum_Unnamed4;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed5 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_SystemMediaType {
SYSTEM_MEDIATYPE_CTR_NAND = 0,
SYSTEM_MEDIATYPE_TWL_NAND = 1,
SYSTEM_MEDIATYPE_SD = 2,
SYSTEM_MEDIATYPE_TWL_PHOTO = 3,
}
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_ArchiveID {
ARCHIVE_ROMFS = 3,
ARCHIVE_SAVEDATA = 4,
ARCHIVE_EXTDATA = 6,
@ -57,33 +58,31 @@ pub enum Enum_Unnamed5 { @@ -57,33 +58,31 @@ pub enum Enum_Unnamed5 {
ARCHIVE_SAVEDATA_AND_CONTENT2 = 591751054,
ARCHIVE_NAND_CTR_FS = 1450741931,
ARCHIVE_TWL_PHOTO = 1450741932,
ARCHIVE_TWL_SOUND = 1450741933,
ARCHIVE_NAND_TWL_FS = 1450741934,
ARCHIVE_NAND_W_FS = 1450741935,
ARCHIVE_GAMECARD_SAVEDATA = 1450741937,
ARCHIVE_USER_SAVEDATA = 1450741938,
ARCHIVE_DEMO_SAVEDATA = 1450741940,
}
pub type FS_ArchiveID = Enum_Unnamed5;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed6 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_PathType {
PATH_INVALID = 0,
PATH_EMPTY = 1,
PATH_BINARY = 2,
PATH_ASCII = 3,
PATH_UTF16 = 4,
}
pub type FS_PathType = Enum_Unnamed6;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed7 { SECUREVALUE_SLOT_SD = 4096, }
pub type FS_SecureValueSlot = Enum_Unnamed7;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed8 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_SecureValueSlot { SECUREVALUE_SLOT_SD = 4096, }
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_CardSpiBaudRate {
BAUDRATE_512KHZ = 0,
BAUDRATE_1MHZ = 1,
BAUDRATE_2MHZ = 2,
@ -91,64 +90,51 @@ pub enum Enum_Unnamed8 { @@ -91,64 +90,51 @@ pub enum Enum_Unnamed8 {
BAUDRATE_8MHZ = 4,
BAUDRATE_16MHZ = 5,
}
pub type FS_CardSpiBaudRate = Enum_Unnamed8;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed9 {
BUSMODE_1BIT = 0,
BUSMODE_4BIT = 1,
}
pub type FS_CardSpiBusMode = Enum_Unnamed9;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed10 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_CardSpiBusMode { BUSMODE_1BIT = 0, BUSMODE_4BIT = 1, }
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_SpecialContentType {
SPECIALCONTENT_UPDATE = 1,
SPECIALCONTENT_MANUAL = 2,
SPECIALCONTENT_DLP_CHILD = 3,
}
pub type FS_SpecialContentType = Enum_Unnamed10;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed11 { CARD_CTR = 0, CARD_TWL = 1, }
pub type FS_CardType = Enum_Unnamed11;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed12 { FS_ACTION_UNKNOWN = 0, }
pub type FS_Action = Enum_Unnamed12;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed13 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_CardType { CARD_CTR = 0, CARD_TWL = 1, }
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_Action { FS_ACTION_UNKNOWN = 0, }
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_ArchiveAction {
ARCHIVE_ACTION_COMMIT_SAVE_DATA = 0,
ARCHIVE_ACTION_GET_TIMESTAMP = 1,
}
pub type FS_ArchiveAction = Enum_Unnamed13;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed14 {
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_SecureSaveAction {
SECURESAVE_ACTION_DELETE = 0,
SECURESAVE_ACTION_FORMAT = 1,
}
pub type FS_SecureSaveAction = Enum_Unnamed14;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed15 { FILE_ACTION_UNKNOWN = 0, }
pub type FS_FileAction = Enum_Unnamed15;
#[derive(Clone, Copy)]
#[repr(C)]
pub enum Enum_Unnamed16 { DIRECTORY_ACTION_UNKNOWN = 0, }
pub type FS_DirectoryAction = Enum_Unnamed16;
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_FileAction { FILE_ACTION_UNKNOWN = 0, }
#[derive(Copy, Clone)]
#[repr(u32)]
#[derive(Debug)]
pub enum FS_DirectoryAction { DIRECTORY_ACTION_UNKNOWN = 0, }
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed17 {
pub struct FS_DirectoryEntry {
pub name: [u16; 262usize],
pub shortName: [u8; 10usize],
pub shortExt: [u8; 4usize],
@ -157,169 +143,175 @@ pub struct Struct_Unnamed17 { @@ -157,169 +143,175 @@ pub struct Struct_Unnamed17 {
pub attributes: u32,
pub fileSize: u64,
}
impl ::core::clone::Clone for Struct_Unnamed17 {
impl ::core::clone::Clone for FS_DirectoryEntry {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed17 {
impl ::core::default::Default for FS_DirectoryEntry {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_DirectoryEntry = Struct_Unnamed17;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed18 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_ArchiveResource {
pub sectorSize: u32,
pub clusterSize: u32,
pub totalClusters: u32,
pub freeClusters: u32,
}
impl ::core::clone::Clone for Struct_Unnamed18 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed18 {
impl ::core::default::Default for FS_ArchiveResource {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_ArchiveResource = Struct_Unnamed18;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed19 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_ProgramInfo {
pub programId: u64,
pub _bindgen_bitfield_1_: FS_MediaType,
pub padding: [u8; 7usize],
}
impl ::core::clone::Clone for Struct_Unnamed19 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed19 {
impl ::core::default::Default for FS_ProgramInfo {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_ProgramInfo = Struct_Unnamed19;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed20 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_ProductInfo {
pub productCode: [u8; 16usize],
pub companyCode: [u8; 2usize],
pub remasterVersion: u16,
}
impl ::core::clone::Clone for Struct_Unnamed20 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed20 {
impl ::core::default::Default for FS_ProductInfo {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_ProductInfo = Struct_Unnamed20;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed21 {
pub struct FS_IntegrityVerificationSeed {
pub aesCbcMac: [u8; 16usize],
pub movableSed: [u8; 288usize],
}
impl ::core::clone::Clone for Struct_Unnamed21 {
impl ::core::clone::Clone for FS_IntegrityVerificationSeed {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed21 {
impl ::core::default::Default for FS_IntegrityVerificationSeed {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_IntegrityVerificationSeed = Struct_Unnamed21;
#[repr(C, packed)]
#[derive(Copy)]
pub struct Struct_Unnamed22 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_ExtSaveDataInfo {
pub _bindgen_bitfield_1_: FS_MediaType,
pub unknown: u8,
pub reserved1: u16,
pub saveId: u64,
pub reserved2: u32,
}
impl ::core::clone::Clone for Struct_Unnamed22 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed22 {
impl ::core::default::Default for FS_ExtSaveDataInfo {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_ExtSaveDataInfo = Struct_Unnamed22;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed23 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_SystemSaveDataInfo {
pub _bindgen_bitfield_1_: FS_MediaType,
pub unknown: u8,
pub reserved: u16,
pub saveId: u32,
}
impl ::core::clone::Clone for Struct_Unnamed23 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed23 {
impl ::core::default::Default for FS_SystemSaveDataInfo {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_SystemSaveDataInfo = Struct_Unnamed23;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed24 {
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_DeviceMoveContext {
pub ivs: [u8; 16usize],
pub encryptParameter: [u8; 16usize],
}
impl ::core::clone::Clone for Struct_Unnamed24 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed24 {
impl ::core::default::Default for FS_DeviceMoveContext {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_DeviceMoveContext = Struct_Unnamed24;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed25 {
pub _type: FS_PathType,
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct FS_Path {
pub type_: FS_PathType,
pub size: u32,
pub data: *const c_void,
pub data: *const ::libc::c_void,
}
impl ::core::clone::Clone for Struct_Unnamed25 {
fn clone(&self) -> Self { *self }
}
impl ::core::default::Default for Struct_Unnamed25 {
impl ::core::default::Default for FS_Path {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_Path = Struct_Unnamed25;
pub type FS_Archive = u64;
#[repr(C)]
#[derive(Copy)]
pub struct Struct_Unnamed26 {
pub id: u32,
pub lowPath: FS_Path,
pub handle: u64,
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct romfs_header {
pub headerSize: u32,
pub dirHashTableOff: u32,
pub dirHashTableSize: u32,
pub dirTableOff: u32,
pub dirTableSize: u32,
pub fileHashTableOff: u32,
pub fileHashTableSize: u32,
pub fileTableOff: u32,
pub fileTableSize: u32,
pub fileDataOff: u32,
}
impl ::core::default::Default for romfs_header {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
impl ::core::clone::Clone for Struct_Unnamed26 {
fn clone(&self) -> Self { *self }
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct romfs_dir {
pub parent: u32,
pub sibling: u32,
pub childDir: u32,
pub childFile: u32,
pub nextHash: u32,
pub nameLen: u32,
pub name: [u16; 0usize],
}
impl ::core::default::Default for romfs_dir {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
impl ::core::default::Default for Struct_Unnamed26 {
#[repr(C)]
#[derive(Copy, Clone)]
#[derive(Debug)]
pub struct romfs_file {
pub parent: u32,
pub sibling: u32,
pub dataOff: u64,
pub dataSize: u64,
pub nextHash: u32,
pub nameLen: u32,
pub name: [u16; 0usize],
}
impl ::core::default::Default for romfs_file {
fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub type FS_Archive = Struct_Unnamed26;
pub enum romfs_mount { }
extern "C" {
pub fn fsInit() -> Result;
pub fn fsExit();
pub fn fsUseSession(session: Handle, sdmc: u8);
pub fn fsUseSession(session: Handle);
pub fn fsEndUseSession();
pub fn fsMakePath(_type: FS_PathType, path: *const c_void)
pub fn fsExemptFromSession(archive: FS_Archive);
pub fn fsUnexemptFromSession(archive: FS_Archive);
pub fn fsMakePath(type_: FS_PathType, path: *const ::libc::c_void)
-> FS_Path;
pub fn fsGetSessionHandle() -> *mut Handle;
pub fn FSUSER_Control(action: FS_Action,
input: *mut c_void, inputSize: u32,
output: *mut c_void,
pub fn FSUSER_Control(action: FS_Action, input: *mut ::libc::c_void,
inputSize: u32, output: *mut ::libc::c_void,
outputSize: u32) -> Result;
pub fn FSUSER_Initialize(session: Handle) -> Result;
pub fn FSUSER_OpenFile(out: *mut Handle, archive: FS_Archive,
path: FS_Path, openFlags: u32, attributes: u32)
-> Result;
pub fn FSUSER_OpenFileDirectly(out: *mut Handle, archive: FS_Archive,
path: FS_Path, openFlags: u32,
attributes: u32) -> Result;
pub fn FSUSER_OpenFileDirectly(out: *mut Handle, archiveId: FS_ArchiveID,
archivePath: FS_Path, filePath: FS_Path,
openFlags: u32, attributes: u32)
-> Result;
pub fn FSUSER_DeleteFile(archive: FS_Archive, path: FS_Path) -> Result;
pub fn FSUSER_RenameFile(srcArchive: FS_Archive, srcPath: FS_Path,
dstArchive: FS_Archive, dstPath: FS_Path)
@ -337,17 +329,17 @@ extern "C" { @@ -337,17 +329,17 @@ extern "C" {
-> Result;
pub fn FSUSER_OpenDirectory(out: *mut Handle, archive: FS_Archive,
path: FS_Path) -> Result;
pub fn FSUSER_OpenArchive(archive: *mut FS_Archive) -> Result;
pub fn FSUSER_OpenArchive(archive: *mut FS_Archive, id: FS_ArchiveID,
path: FS_Path) -> Result;
pub fn FSUSER_ControlArchive(archive: FS_Archive,
action: FS_ArchiveAction,
input: *mut c_void,
inputSize: u32,
output: *mut c_void,
input: *mut ::libc::c_void, inputSize: u32,
output: *mut ::libc::c_void,
outputSize: u32) -> Result;
pub fn FSUSER_CloseArchive(archive: *mut FS_Archive) -> Result;
pub fn FSUSER_CloseArchive(archive: FS_Archive) -> Result;
pub fn FSUSER_GetFreeBytes(freeBytes: *mut u64, archive: FS_Archive)
-> Result;
pub fn FSUSER_GetCardType(_type: *mut FS_CardType) -> Result;
pub fn FSUSER_GetCardType(type_: *mut FS_CardType) -> Result;
pub fn FSUSER_GetSdmcArchiveResource(archiveResource:
*mut FS_ArchiveResource)
-> Result;
@ -401,12 +393,13 @@ extern "C" { @@ -401,12 +393,13 @@ extern "C" {
pub fn FSUSER_GetSpecialContentIndex(index: *mut u16,
mediaType: FS_MediaType,
programId: u64,
_type: FS_SpecialContentType)
type_: FS_SpecialContentType)
-> Result;
pub fn FSUSER_GetLegacyRomHeader(mediaType: FS_MediaType, programId: u64,
header: *mut u8) -> Result;
pub fn FSUSER_GetLegacyBannerData(mediaType: FS_MediaType, programId: u64,
banner: *mut u8) -> Result;
pub fn FSUSER_GetLegacyBannerData(mediaType: FS_MediaType,
programId: u64, banner: *mut u8)
-> Result;
pub fn FSUSER_CheckAuthorityToAccessExtSaveData(access: *mut u8,
mediaType: FS_MediaType,
saveId: u64,
@ -425,11 +418,12 @@ extern "C" { @@ -425,11 +418,12 @@ extern "C" {
archiveId: FS_ArchiveID, path: FS_Path)
-> Result;
pub fn FSUSER_GetLegacyRomHeader2(headerSize: u32,
mediaType: FS_MediaType, programId: u64,
header: *mut u8) -> Result;
mediaType: FS_MediaType,
programId: u64, header: *mut u8)
-> Result;
pub fn FSUSER_GetSdmcCtrRootPath(out: *mut u8, length: u32) -> Result;
pub fn FSUSER_GetArchiveResource(archiveResource: *mut FS_ArchiveResource,
mediaType: FS_MediaType) -> Result;
mediaType: FS_SystemMediaType) -> Result;
pub fn FSUSER_ExportIntegrityVerificationSeed(seed:
*mut FS_IntegrityVerificationSeed)
-> Result;
@ -482,13 +476,14 @@ extern "C" { @@ -482,13 +476,14 @@ extern "C" {
emulateEndurance: u8) -> Result;
pub fn FSUSER_SwitchCleanupInvalidSaveData(enable: u8) -> Result;
pub fn FSUSER_EnumerateSystemSaveData(idsWritten: *mut u32,
idsSize: u32, ids: *mut u64)
idsSize: u32, ids: *mut u32)
-> Result;
pub fn FSUSER_InitializeWithSdkVersion(session: Handle, version: u32)
-> Result;
pub fn FSUSER_SetPriority(priority: u32) -> Result;
pub fn FSUSER_GetPriority(priority: *mut u32) -> Result;
pub fn FSUSER_SetSaveDataSecureValue(value: u64, slot: FS_SecureValueSlot,
pub fn FSUSER_SetSaveDataSecureValue(value: u64,
slot: FS_SecureValueSlot,
titleUniqueId: u32,
titleVariation: u8) -> Result;
pub fn FSUSER_GetSaveDataSecureValue(exists: *mut u8, value: *mut u64,
@ -496,22 +491,21 @@ extern "C" { @@ -496,22 +491,21 @@ extern "C" {
titleUniqueId: u32,
titleVariation: u8) -> Result;
pub fn FSUSER_ControlSecureSave(action: FS_SecureSaveAction,
input: *mut c_void,
input: *mut ::libc::c_void,
inputSize: u32,
output: *mut c_void,
output: *mut ::libc::c_void,
outputSize: u32) -> Result;
pub fn FSUSER_GetMediaType(mediaType: *mut FS_MediaType) -> Result;
pub fn FSFILE_Control(handle: Handle, action: FS_FileAction,
input: *mut c_void, inputSize: u32,
output: *mut c_void,
outputSize: u32) -> Result;
input: *mut ::libc::c_void, inputSize: u32,
output: *mut ::libc::c_void, outputSize: u32)
-> Result;
pub fn FSFILE_OpenSubFile(handle: Handle, subFile: *mut Handle,
offset: u64, size: u64) -> Result;
pub fn FSFILE_Read(handle: Handle, bytesRead: *mut u32, offset: u64,
buffer: *mut c_void, size: u32)
-> Result;
buffer: *mut ::libc::c_void, size: u32) -> Result;
pub fn FSFILE_Write(handle: Handle, bytesWritten: *mut u32, offset: u64,
buffer: *const c_void, size: u32,
buffer: *const ::libc::c_void, size: u32,
flags: u32) -> Result;
pub fn FSFILE_GetSize(handle: Handle, size: *mut u64) -> Result;
pub fn FSFILE_SetSize(handle: Handle, size: u64) -> Result;
@ -525,13 +519,18 @@ extern "C" { @@ -525,13 +519,18 @@ extern "C" {
pub fn FSFILE_OpenLinkFile(handle: Handle, linkFile: *mut Handle)
-> Result;
pub fn FSDIR_Control(handle: Handle, action: FS_DirectoryAction,
input: *mut c_void, inputSize: u32,
output: *mut c_void,
outputSize: u32) -> Result;
input: *mut ::libc::c_void, inputSize: u32,
output: *mut ::libc::c_void, outputSize: u32)
-> Result;
pub fn FSDIR_Read(handle: Handle, entriesRead: *mut u32,
entryCount: u32, entries: *mut FS_DirectoryEntry)
-> Result;
pub fn FSDIR_Close(handle: Handle) -> Result;
pub fn FSDIR_SetPriority(handle: Handle, priority: u32) -> Result;
pub fn FSDIR_GetPriority(handle: Handle, priority: *mut u32) -> Result;
pub fn romfsMount(mount: *mut *mut romfs_mount) -> Result;
pub fn romfsMountFromFile(file: Handle, offset: u32,
mount: *mut *mut romfs_mount) -> Result;
pub fn romfsBind(mount: *mut romfs_mount) -> Result;
pub fn romfsUnmount(mount: *mut romfs_mount) -> Result;
}

554
src/ascii.rs

@ -0,0 +1,554 @@ @@ -0,0 +1,554 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Operations on ASCII strings and characters.
use core::mem;
use core::ops::Range;
use collections::{String, Vec};
/// Extension methods for ASCII-subset only operations on string slices.
///
/// Be aware that operations on seemingly non-ASCII characters can sometimes
/// have unexpected results. Consider this example:
///
/// ```
/// use std::ascii::AsciiExt;
///
/// assert_eq!("café".to_ascii_uppercase(), "CAFÉ");
/// assert_eq!("café".to_ascii_uppercase(), "CAFé");
/// ```
///
/// In the first example, the lowercased string is represented `"cafe\u{301}"`
/// (the last character is an acute accent [combining character]). Unlike the
/// other characters in the string, the combining character will not get mapped
/// to an uppercase variant, resulting in `"CAFE\u{301}"`. In the second
/// example, the lowercased string is represented `"caf\u{e9}"` (the last
/// character is a single Unicode character representing an 'e' with an acute
/// accent). Since the last character is defined outside the scope of ASCII,
/// it will not get mapped to an uppercase variant, resulting in `"CAF\u{e9}"`.
///
/// [combining character]: https://en.wikipedia.org/wiki/Combining_character
pub trait AsciiExt {
/// Container type for copied ASCII characters.
type Owned;
/// Checks if the value is within the ASCII range.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii = 'a';
/// let utf8 = '❤';
///
/// assert!(ascii.is_ascii());
/// assert!(!utf8.is_ascii());
/// ```
fn is_ascii(&self) -> bool;
/// Makes a copy of the string in ASCII upper case.
///
/// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z',
/// but non-ASCII letters are unchanged.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii = 'a';
/// let utf8 = '❤';
///
/// assert_eq!('A', ascii.to_ascii_uppercase());
/// assert_eq!('❤', utf8.to_ascii_uppercase());
/// ```
fn to_ascii_uppercase(&self) -> Self::Owned;
/// Makes a copy of the string in ASCII lower case.
///
/// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z',
/// but non-ASCII letters are unchanged.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii = 'A';
/// let utf8 = '❤';
///
/// assert_eq!('a', ascii.to_ascii_lowercase());
/// assert_eq!('❤', utf8.to_ascii_lowercase());
/// ```
fn to_ascii_lowercase(&self) -> Self::Owned;
/// Checks that two strings are an ASCII case-insensitive match.
///
/// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
/// but without allocating and copying temporary strings.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let ascii1 = 'A';
/// let ascii2 = 'a';
/// let ascii3 = 'A';
/// let ascii4 = 'z';
///
/// assert!(ascii1.eq_ignore_ascii_case(&ascii2));
/// assert!(ascii1.eq_ignore_ascii_case(&ascii3));
/// assert!(!ascii1.eq_ignore_ascii_case(&ascii4));
/// ```
fn eq_ignore_ascii_case(&self, other: &Self) -> bool;
/// Converts this type to its ASCII upper case equivalent in-place.
///
/// See `to_ascii_uppercase` for more information.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let mut ascii = 'a';
///
/// ascii.make_ascii_uppercase();
///
/// assert_eq!('A', ascii);
/// ```
fn make_ascii_uppercase(&mut self);
/// Converts this type to its ASCII lower case equivalent in-place.
///
/// See `to_ascii_lowercase` for more information.
///
/// # Examples
///
/// ```
/// use std::ascii::AsciiExt;
///
/// let mut ascii = 'A';
///
/// ascii.make_ascii_lowercase();
///
/// assert_eq!('a', ascii);
/// ```
fn make_ascii_lowercase(&mut self);
}
impl AsciiExt for str {
type Owned = String;
#[inline]
fn is_ascii(&self) -> bool {
self.bytes().all(|b| b.is_ascii())
}
#[inline]
fn to_ascii_uppercase(&self) -> String {
let mut bytes = self.as_bytes().to_vec();
bytes.make_ascii_uppercase();
// make_ascii_uppercase() preserves the UTF-8 invariant.
unsafe { String::from_utf8_unchecked(bytes) }
}
#[inline]
fn to_ascii_lowercase(&self) -> String {
let mut bytes = self.as_bytes().to_vec();
bytes.make_ascii_lowercase();
// make_ascii_uppercase() preserves the UTF-8 invariant.
unsafe { String::from_utf8_unchecked(bytes) }
}
#[inline]
fn eq_ignore_ascii_case(&self, other: &str) -> bool {
self.as_bytes().eq_ignore_ascii_case(other.as_bytes())
}
fn make_ascii_uppercase(&mut self) {
let me: &mut [u8] = unsafe { mem::transmute(self) };
me.make_ascii_uppercase()
}
fn make_ascii_lowercase(&mut self) {
let me: &mut [u8] = unsafe { mem::transmute(self) };
me.make_ascii_lowercase()
}
}
impl AsciiExt for [u8] {
type Owned = Vec<u8>;
#[inline]
fn is_ascii(&self) -> bool {
self.iter().all(|b| b.is_ascii())
}
#[inline]
fn to_ascii_uppercase(&self) -> Vec<u8> {
let mut me = self.to_vec();
me.make_ascii_uppercase();
return me
}
#[inline]
fn to_ascii_lowercase(&self) -> Vec<u8> {
let mut me = self.to_vec();
me.make_ascii_lowercase();
return me
}
#[inline]
fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool {
self.len() == other.len() &&
self.iter().zip(other).all(|(a, b)| {
a.eq_ignore_ascii_case(b)
})
}
fn make_ascii_uppercase(&mut self) {
for byte in self {
byte.make_ascii_uppercase();
}
}
fn make_ascii_lowercase(&mut self) {
for byte in self {
byte.make_ascii_lowercase();
}
}
}
impl AsciiExt for u8 {
type Owned = u8;
#[inline]
fn is_ascii(&self) -> bool { *self & 128 == 0 }
#[inline]
fn to_ascii_uppercase(&self) -> u8 { ASCII_UPPERCASE_MAP[*self as usize] }
#[inline]
fn to_ascii_lowercase(&self) -> u8 { ASCII_LOWERCASE_MAP[*self as usize] }
#[inline]
fn eq_ignore_ascii_case(&self, other: &u8) -> bool {
self.to_ascii_lowercase() == other.to_ascii_lowercase()
}
#[inline]
fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); }
#[inline]
fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); }
}
impl AsciiExt for char {
type Owned = char;
#[inline]
fn is_ascii(&self) -> bool {
*self as u32 <= 0x7F
}
#[inline]
fn to_ascii_uppercase(&self) -> char {
if self.is_ascii() {
(*self as u8).to_ascii_uppercase() as char
} else {
*self
}
}
#[inline]
fn to_ascii_lowercase(&self) -> char {
if self.is_ascii() {
(*self as u8).to_ascii_lowercase() as char
} else {
*self
}
}
#[inline]
fn eq_ignore_ascii_case(&self, other: &char) -> bool {
self.to_ascii_lowercase() == other.to_ascii_lowercase()
}
#[inline]
fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); }
#[inline]
fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); }
}
/// An iterator over the escaped version of a byte, constructed via
/// `std::ascii::escape_default`.
pub struct EscapeDefault {
range: Range<usize>,
data: [u8; 4],
}
/// Returns an iterator that produces an escaped version of a `u8`.
///
/// The default is chosen with a bias toward producing literals that are
/// legal in a variety of languages, including C++11 and similar C-family
/// languages. The exact rules are:
///
/// - Tab, CR and LF are escaped as '\t', '\r' and '\n' respectively.
/// - Single-quote, double-quote and backslash chars are backslash-escaped.
/// - Any other chars in the range [0x20,0x7e] are not escaped.
/// - Any other chars are given hex escapes of the form '\xNN'.
/// - Unicode escapes are never generated by this function.
///
/// # Examples
///
/// ```
/// use std::ascii;
///
/// let escaped = ascii::escape_default(b'0').next().unwrap();
/// assert_eq!(b'0', escaped);
///
/// let mut escaped = ascii::escape_default(b'\t');
///
/// assert_eq!(b'\\', escaped.next().unwrap());
/// assert_eq!(b't', escaped.next().unwrap());
/// ```
pub fn escape_default(c: u8) -> EscapeDefault {
let (data, len) = match c {
b'\t' => ([b'\\', b't', 0, 0], 2),
b'\r' => ([b'\\', b'r', 0, 0], 2),
b'\n' => ([b'\\', b'n', 0, 0], 2),
b'\\' => ([b'\\', b'\\', 0, 0], 2),
b'\'' => ([b'\\', b'\'', 0, 0], 2),
b'"' => ([b'\\', b'"', 0, 0], 2),
b'\x20' ... b'\x7e' => ([c, 0, 0, 0], 1),
_ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4),
};
return EscapeDefault { range: (0.. len), data: data };
fn hexify(b: u8) -> u8 {
match b {
0 ... 9 => b'0' + b,
_ => b'a' + b - 10,
}
}
}
impl Iterator for EscapeDefault {
type Item = u8;
fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) }
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl DoubleEndedIterator for EscapeDefault {
fn next_back(&mut self) -> Option<u8> {
self.range.next_back().map(|i| self.data[i])
}
}
impl ExactSizeIterator for EscapeDefault {}
static ASCII_LOWERCASE_MAP: [u8; 256] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
b'@',
b'a', b'b', b'c', b'd', b'e', b'f', b'g',
b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
b'x', b'y', b'z',
b'[', b'\\', b']', b'^', b'_',
b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g',
b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o',
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w',
b'x', b'y', b'z', b'{', b'|', b'}', b'~', 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
static ASCII_UPPERCASE_MAP: [u8; 256] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
b' ', b'!', b'"', b'#', b'$', b'%', b'&', b'\'',
b'(', b')', b'*', b'+', b',', b'-', b'.', b'/',
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
b'8', b'9', b':', b';', b'<', b'=', b'>', b'?',
b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G',
b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
b'X', b'Y', b'Z', b'[', b'\\', b']', b'^', b'_',
b'`',
b'A', b'B', b'C', b'D', b'E', b'F', b'G',
b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O',
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
b'X', b'Y', b'Z',
b'{', b'|', b'}', b'~', 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
#[cfg(test)]
mod tests {
use super::*;
use rustc_unicode::char::from_u32;
use collections::string::ToString;
#[test]
fn test_is_ascii() {
assert!(b"".is_ascii());
assert!(b"banana\0\x7F".is_ascii());
assert!(b"banana\0\x7F".iter().all(|b| b.is_ascii()));
assert!(!b"Vi\xe1\xbb\x87t Nam".is_ascii());
assert!(!b"Vi\xe1\xbb\x87t Nam".iter().all(|b| b.is_ascii()));
assert!(!b"\xe1\xbb\x87".iter().any(|b| b.is_ascii()));
assert!("".is_ascii());
assert!("banana\0\u{7F}".is_ascii());
assert!("banana\0\u{7F}".chars().all(|c| c.is_ascii()));
assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii()));
// NOTE: This test fails for some reason.
assert!(!"ประเทศไทย中华ệ ".chars().any(|c| c.is_ascii()));
}
#[test]
fn test_to_ascii_uppercase() {
assert_eq!("url()URL()uRl()ürl".to_ascii_uppercase(), "URL()URL()URL()üRL");
assert_eq!("hıKß".to_ascii_uppercase(), "HıKß");
for i in 0..501 {
let upper = if 'a' as u32 <= i && i <= 'z' as u32 { i + 'A' as u32 - 'a' as u32 }
else { i };
assert_eq!((from_u32(i).unwrap()).to_string().to_ascii_uppercase(),
(from_u32(upper).unwrap()).to_string());
}
}
#[test]
fn test_to_ascii_lowercase() {
assert_eq!("url()URL()uRl()Ürl".to_ascii_lowercase(), "url()url()url()Ürl");
// Dotted capital I, Kelvin sign, Sharp S.
assert_eq!("HİKß".to_ascii_lowercase(), "hİKß");
for i in 0..501 {
let lower = if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 }
else { i };
assert_eq!((from_u32(i).unwrap()).to_string().to_ascii_lowercase(),
(from_u32(lower).unwrap()).to_string());
}
}
#[test]
fn test_make_ascii_lower_case() {
macro_rules! test {
($from: expr, $to: expr) => {
{
let mut x = $from;
x.make_ascii_lowercase();
assert_eq!(x, $to);
}
}
}
test!(b'A', b'a');
test!(b'a', b'a');
test!(b'!', b'!');
test!('A', 'a');
test!('À', 'À');
test!('a', 'a');
test!('!', '!');
test!(b"H\xc3\x89".to_vec(), b"h\xc3\x89");
test!("HİKß".to_string(), "hİKß");
}
#[test]
fn test_make_ascii_upper_case() {
macro_rules! test {
($from: expr, $to: expr) => {
{
let mut x = $from;
x.make_ascii_uppercase();
assert_eq!(x, $to);
}
}
}
test!(b'a', b'A');
test!(b'A', b'A');
test!(b'!', b'!');
test!('a', 'A');
test!('à', 'à');
test!('A', 'A');
test!('!', '!');
test!(b"h\xc3\xa9".to_vec(), b"H\xc3\xa9");
test!("hıKß".to_string(), "HıKß");
let mut x = "Hello".to_string();
x[..3].make_ascii_uppercase(); // Test IndexMut on String.
assert_eq!(x, "HELlo")
}
#[test]
fn test_eq_ignore_ascii_case() {
assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl"));
assert!(!"Ürl".eq_ignore_ascii_case("ürl"));
// Dotted capital I, Kelvin sign, Sharp S.
assert!("HİKß".eq_ignore_ascii_case("hİKß"));
assert!(!"İ".eq_ignore_ascii_case("i"));
assert!(!"K".eq_ignore_ascii_case("k"));
assert!(!"ß".eq_ignore_ascii_case("s"));
for i in 0..501 {
let lower = if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 }
else { i };
assert!((from_u32(i).unwrap()).to_string().eq_ignore_ascii_case(
&from_u32(lower).unwrap().to_string()));
}
}
#[test]
fn inference_works() {
let x = "a".to_string();
x.eq_ignore_ascii_case("A");
}
}

3
src/ffi/mod.rs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
pub use self::os_str::{OsString, OsStr};
mod os_str;

576
src/ffi/os_str.rs

@ -0,0 +1,576 @@ @@ -0,0 +1,576 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use collections::borrow::{Borrow, Cow, ToOwned};
use core::fmt::{self, Debug};
use core::mem;
use collections::String;
use core::ops;
use core::cmp;
use core::hash::{Hash, Hasher};
use collections::Vec;
use sys::wtf8::{Wtf8, Wtf8Buf};
use sys::{AsInner, IntoInner, FromInner};
pub use sys::wtf8::EncodeWide;
/// A type that can represent owned, mutable platform-native strings, but is
/// cheaply inter-convertible with Rust strings.
///
/// The need for this type arises from the fact that:
///
/// * On Unix systems, strings are often arbitrary sequences of non-zero
/// bytes, in many cases interpreted as UTF-8.
///
/// * On Windows, strings are often arbitrary sequences of non-zero 16-bit
/// values, interpreted as UTF-16 when it is valid to do so.
///
/// * In Rust, strings are always valid UTF-8, but may contain zeros.
///
/// `OsString` and `OsStr` bridge this gap by simultaneously representing Rust
/// and platform-native string values, and in particular allowing a Rust string
/// to be converted into an "OS" string with no cost.
#[derive(Clone)]
pub struct OsString {
inner: Wtf8Buf
}
/// Slices into OS strings (see `OsString`).
pub struct OsStr {
inner: Wtf8
}
impl OsString {
/// Constructs a new empty `OsString`.
pub fn new() -> OsString {
OsString { inner: Wtf8Buf::from_string(String::new()) }
}
fn _from_bytes(vec: Vec<u8>) -> Option<OsString> {
String::from_utf8(vec).ok().map(OsString::from)
}
/// Converts to an `OsStr` slice.
pub fn as_os_str(&self) -> &OsStr {
self
}
/// Converts the `OsString` into a `String` if it contains valid Unicode data.
///
/// On failure, ownership of the original `OsString` is returned.
pub fn into_string(self) -> Result<String, OsString> {
self.inner.into_string().map_err(|buf| OsString { inner: buf} )
}
/// Extends the string with the given `&OsStr` slice.
pub fn push<T: AsRef<OsStr>>(&mut self, s: T) {
self.inner.push_wtf8(&s.as_ref().inner)
}
/// Creates a new `OsString` with the given capacity.
///
/// The string will be able to hold exactly `capacity` lenth units of other
/// OS strings without reallocating. If `capacity` is 0, the string will not
/// allocate.
///
/// See main `OsString` documentation information about encoding.
pub fn with_capacity(capacity: usize) -> OsString {
OsString {
inner: Wtf8Buf::with_capacity(capacity)
}
}
/// Truncates the `OsString` to zero length.
pub fn clear(&mut self) {
self.inner.clear()
}
/// Returns the capacity this `OsString` can hold without reallocating.
///
/// See `OsString` introduction for information about encoding.
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
/// Reserves capacity for at least `additional` more capacity to be inserted
/// in the given `OsString`.
///
/// The collection may reserve more space to avoid frequent reallocations.
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional)
}
/// Reserves the minimum capacity for exactly `additional` more capacity to
/// be inserted in the given `OsString`. Does nothing if the capacity is
/// already sufficient.
///
/// Note that the allocator may give the collection more space than it
/// requests. Therefore capacity can not be relied upon to be precisely
/// minimal. Prefer reserve if future insertions are expected.
pub fn reserve_exact(&mut self, additional: usize) {
self.inner.reserve_exact(additional)
}
/// Creates an `OsString` from a potentially ill-formed UTF-16 slice of
/// 16-bit code units.
///
/// This is lossless: calling `.encode_wide()` on the resulting string
/// will always return the original code units.
///
/// NOTE: This function was copied from the windows implementation of OsStringExt
pub fn from_wide(wide: &[u16]) -> OsString {
OsString { inner: Wtf8Buf::from_wide(wide) }
}
}
impl From<String> for OsString {
fn from(s: String) -> OsString {
OsString { inner: Wtf8Buf::from_string(s) }
}
}
impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for OsString {
fn from(s: &'a T) -> OsString {
s.as_ref().to_os_string()
}
}
impl ops::Index<ops::RangeFull> for OsString {
type Output = OsStr;
#[inline]
fn index(&self, _index: ops::RangeFull) -> &OsStr {
OsStr::from_inner(self.inner.as_slice())
}
}
impl ops::Deref for OsString {
type Target = OsStr;
#[inline]
fn deref(&self) -> &OsStr {
&self[..]
}
}
impl Default for OsString {
#[inline]
fn default() -> OsString {
OsString::new()
}
}
impl Debug for OsString {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&**self, formatter)
}
}
impl PartialEq for OsString {
fn eq(&self, other: &OsString) -> bool {
&**self == &**other
}
}
impl PartialEq<str> for OsString {
fn eq(&self, other: &str) -> bool {
&**self == other
}
}
impl PartialEq<OsString> for str {
fn eq(&self, other: &OsString) -> bool {
&**other == self
}
}
impl Eq for OsString {}
impl PartialOrd for OsString {
#[inline]
fn partial_cmp(&self, other: &OsString) -> Option<cmp::Ordering> {
(&**self).partial_cmp(&**other)
}
#[inline]
fn lt(&self, other: &OsString) -> bool { &**self < &**other }
#[inline]
fn le(&self, other: &OsString) -> bool { &**self <= &**other }
#[inline]
fn gt(&self, other: &OsString) -> bool { &**self > &**other }
#[inline]
fn ge(&self, other: &OsString) -> bool { &**self >= &**other }
}
impl PartialOrd<str> for OsString {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
(&**self).partial_cmp(other)
}
}
impl Ord for OsString {
#[inline]
fn cmp(&self, other: &OsString) -> cmp::Ordering {
(&**self).cmp(&**other)
}
}
impl Hash for OsString {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
(&**self).hash(state)
}
}
impl OsStr {
/// Coerces into an `OsStr` slice.
pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
s.as_ref()
}
fn from_inner(inner: &Wtf8) -> &OsStr {
unsafe { mem::transmute(inner) }
}
/// Yields a `&str` slice if the `OsStr` is valid Unicode.
///
/// This conversion may entail doing a check for UTF-8 validity.
pub fn to_str(&self) -> Option<&str> {
self.inner.as_str()
}
/// Converts an `OsStr` to a `Cow<str>`.
///
/// Any non-Unicode sequences are replaced with U+FFFD REPLACEMENT CHARACTER.
pub fn to_string_lossy(&self) -> Cow<str> {
self.inner.to_string_lossy()
}
/// Copies the slice into an owned `OsString`.
pub fn to_os_string(&self) -> OsString {
let mut buf = Wtf8Buf::with_capacity(self.inner.len());
buf.push_wtf8(&self.inner);
OsString { inner: buf }
}
/// Checks whether the `OsStr` is empty.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Returns the length of this `OsStr`.
///
/// Note that this does **not** return the number of bytes in this string
/// as, for example, OS strings on Windows are encoded as a list of `u16`
/// rather than a list of bytes. This number is simply useful for passing to
/// other methods like `OsString::with_capacity` to avoid reallocations.
///
/// See `OsStr` introduction for more information about encoding.
pub fn len(&self) -> usize {
self.inner.len()
}
/// Gets the underlying byte representation.
///
/// Note: it is *crucial* that this API is private, to avoid
/// revealing the internal, platform-specific encodings.
fn bytes(&self) -> &[u8] {
unsafe { mem::transmute(&self.inner) }
}
/// Re-encodes an `OsStr` as a wide character sequence,
/// i.e. potentially ill-formed UTF-16.
/// This is lossless. Note that the encoding does not include a final
/// null.
///
/// NOTE: This function was copied from the windows implementation of OsStrExt
pub fn encode_wide(&self) -> EncodeWide {
self.inner.encode_wide()
}
}
impl<'a> Default for &'a OsStr {
#[inline]
fn default() -> &'a OsStr {
OsStr::new("")
}
}
impl PartialEq for OsStr {
fn eq(&self, other: &OsStr) -> bool {
self.bytes().eq(other.bytes())
}
}
impl PartialEq<str> for OsStr {
fn eq(&self, other: &str) -> bool {
*self == *OsStr::new(other)
}
}
impl PartialEq<OsStr> for str {
fn eq(&self, other: &OsStr) -> bool {
*other == *OsStr::new(self)
}
}
impl Eq for OsStr {}
impl PartialOrd for OsStr {
#[inline]
fn partial_cmp(&self, other: &OsStr) -> Option<cmp::Ordering> {
self.bytes().partial_cmp(other.bytes())
}
#[inline]
fn lt(&self, other: &OsStr) -> bool { self.bytes().lt(other.bytes()) }
#[inline]
fn le(&self, other: &OsStr) -> bool { self.bytes().le(other.bytes()) }
#[inline]
fn gt(&self, other: &OsStr) -> bool { self.bytes().gt(other.bytes()) }
#[inline]
fn ge(&self, other: &OsStr) -> bool { self.bytes().ge(other.bytes()) }
}
impl PartialOrd<str> for OsStr {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
self.partial_cmp(OsStr::new(other))
}
}
// FIXME (#19470): cannot provide PartialOrd<OsStr> for str until we
// have more flexible coherence rules.
impl Ord for OsStr {
#[inline]
fn cmp(&self, other: &OsStr) -> cmp::Ordering { self.bytes().cmp(other.bytes()) }
}
macro_rules! impl_cmp {
($lhs:ty, $rhs: ty) => {
impl<'a, 'b> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool { <OsStr as PartialEq>::eq(self, other) }
}
impl<'a, 'b> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool { <OsStr as PartialEq>::eq(self, other) }
}
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<OsStr as PartialOrd>::partial_cmp(self, other)
}
}
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<OsStr as PartialOrd>::partial_cmp(self, other)
}
}
}
}
impl_cmp!(OsString, OsStr);
impl_cmp!(OsString, &'a OsStr);
impl_cmp!(Cow<'a, OsStr>, OsStr);
impl_cmp!(Cow<'a, OsStr>, &'b OsStr);
impl_cmp!(Cow<'a, OsStr>, OsString);
impl Hash for OsStr {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.bytes().hash(state)
}
}
impl Debug for OsStr {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.inner.fmt(formatter)
}
}
impl Borrow<OsStr> for OsString {
fn borrow(&self) -> &OsStr { &self[..] }
}
impl ToOwned for OsStr {
type Owned = OsString;
fn to_owned(&self) -> OsString { self.to_os_string() }
}
impl AsRef<OsStr> for OsStr {
fn as_ref(&self) -> &OsStr {
self
}
}
impl AsRef<OsStr> for OsString {
fn as_ref(&self) -> &OsStr {
self
}
}
impl AsRef<OsStr> for str {
fn as_ref(&self) -> &OsStr {
OsStr::from_inner(Wtf8::from_str(self))
}
}
impl AsRef<OsStr> for String {
fn as_ref(&self) -> &OsStr {
(&**self).as_ref()
}
}
impl FromInner<Wtf8Buf> for OsString {
fn from_inner(buf: Wtf8Buf) -> OsString {
OsString { inner: buf }
}
}
impl IntoInner<Wtf8Buf> for OsString {
fn into_inner(self) -> Wtf8Buf {
self.inner
}
}
impl AsInner<Wtf8> for OsStr {
fn as_inner(&self) -> &Wtf8 {
&self.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
use sys::{AsInner, IntoInner};
#[test]
fn test_os_string_with_capacity() {
let os_string = OsString::with_capacity(0);
assert_eq!(0, os_string.inner.capacity());
let os_string = OsString::with_capacity(10);
assert_eq!(10, os_string.inner.capacity());
let mut os_string = OsString::with_capacity(0);
os_string.push("abc");
assert!(os_string.inner.capacity() >= 3);
}
#[test]
fn test_os_string_clear() {
let mut os_string = OsString::from("abc");
assert_eq!(3, os_string.inner.len());
os_string.clear();
assert_eq!(&os_string, "");
assert_eq!(0, os_string.inner.len());
}
#[test]
fn test_os_string_capacity() {
let os_string = OsString::with_capacity(0);
assert_eq!(0, os_string.capacity());
let os_string = OsString::with_capacity(10);
assert_eq!(10, os_string.capacity());
let mut os_string = OsString::with_capacity(0);
os_string.push("abc");
assert!(os_string.capacity() >= 3);
}
#[test]
fn test_os_string_reserve() {
let mut os_string = OsString::new();
assert_eq!(os_string.capacity(), 0);
os_string.reserve(2);
assert!(os_string.capacity() >= 2);
for _ in 0..16 {
os_string.push("a");
}
assert!(os_string.capacity() >= 16);
os_string.reserve(16);
assert!(os_string.capacity() >= 32);
os_string.push("a");
os_string.reserve(16);
assert!(os_string.capacity() >= 33)
}
#[test]
fn test_os_string_reserve_exact() {
let mut os_string = OsString::new();
assert_eq!(os_string.capacity(), 0);
os_string.reserve_exact(2);
assert!(os_string.capacity() >= 2);
for _ in 0..16 {
os_string.push("a");
}
assert!(os_string.capacity() >= 16);
os_string.reserve_exact(16);
assert!(os_string.capacity() >= 32);
os_string.push("a");
os_string.reserve_exact(16);
assert!(os_string.capacity() >= 33)
}
#[test]
fn test_os_string_default() {
let os_string: OsString = Default::default();
assert_eq!("", &os_string);
}
#[test]
fn test_os_str_is_empty() {
let mut os_string = OsString::new();
assert!(os_string.is_empty());
os_string.push("abc");
assert!(!os_string.is_empty());
os_string.clear();
assert!(os_string.is_empty());
}
#[test]
fn test_os_str_len() {
let mut os_string = OsString::new();
assert_eq!(0, os_string.len());
os_string.push("abc");
assert_eq!(3, os_string.len());
os_string.clear();
assert_eq!(0, os_string.len());
}
#[test]
fn test_os_str_default() {
let os_str: &OsStr = Default::default();
assert_eq!("", os_str);
}
}

24
src/lib.rs

@ -1,20 +1,36 @@ @@ -1,20 +1,36 @@
#![feature(alloc, collections, lang_items)]
#![feature(alloc)]
#![feature(collections)]
#![feature(char_escape_debug)]
#![feature(lang_items)]
#![feature(question_mark)]
#![feature(slice_patterns)]
#![feature(str_internals)]
#![feature(unicode)]
#![no_std]
#![crate_type = "rlib"]
#![crate_name = "ctru"]
extern crate ctru_sys as libctru;
extern crate alloc;
extern crate alloc_system;
extern crate collections;
extern crate rustc_unicode;
extern crate ctru_sys as libctru;
pub mod console;
pub mod srv;
pub mod gfx;
pub mod services;
pub mod sdmc;
pub mod services;
pub mod ascii;
pub mod ffi;
pub mod panic;
pub mod path;
mod sys;
pub use srv::Srv;
pub use gfx::Gfx;

3378
src/path.rs

File diff suppressed because it is too large Load Diff

20
src/services/apt.rs

@ -66,26 +66,6 @@ impl Apt { @@ -66,26 +66,6 @@ impl Apt {
}
}
pub fn get_status(&self) -> AppStatus {
unsafe { apt::aptGetStatus().into() }
}
pub fn set_status(&mut self, status: AppStatus) {
unsafe { apt::aptSetStatus(status.into()) };
}
/// Return to the home menu.
///
/// When `get_status` returns `AppStatus::Suspending`, you should call this,
/// otherwise the app will be left stuck in that state.
///
/// The program will not return from this function until the system returns
/// to the application, or when the status changes to `AppStatus::Exiting`.
pub fn return_to_menu(&mut self) {
unsafe { apt::aptReturnToMenu() }
}
pub fn main_loop(&self) -> bool {
unsafe {
match apt::aptMainLoop() {

892
src/services/fs.rs

@ -0,0 +1,892 @@ @@ -0,0 +1,892 @@
//! Filesystem service
//!
//! This module contains basic methods to manipulate the contents of the 3DS's filesystem.
//! Only the SD card is currently supported.
use core::marker::PhantomData;
use core::ptr;
use core::slice;
use core::mem;
use alloc::arc::Arc;
use collections::Vec;
use path::{Path, PathBuf};
use ffi::OsString;
use libctru::services::fs::*;
#[derive(Copy, Clone, Debug)]
pub enum PathType {
Invalid,
Empty,
Binary,
ASCII,
UTF16,
}
#[derive(Copy, Clone, Debug)]
pub enum ArchiveID {
RomFS,
Savedata,
Extdata,
SharedExtdata,
SystemSavedata,
Sdmc,
SdmcWriteOnly,
BossExtdata,
CardSpiFS,
ExtDataAndBossExtdata,
SystemSaveData2,
NandRW,
NandRO,
NandROWriteAccess,
SaveDataAndContent,
SaveDataAndContent2,
NandCtrFS,
TwlPhoto,
NandTwlFS,
GameCardSavedata,
UserSavedata,
DemoSavedata,
}
/// Represents the filesystem service. No file IO can be performed
/// until an instance of this struct is created.
///
/// The service exits when all instances of this struct go out of scope.
pub struct Fs {
pd: PhantomData<i32>,
}
/// Handle to an open filesystem archive.
///
/// Archives are automatically closed when they go out of scope.
///
/// # Examples
///
/// ```no_run
/// use ctru::services::fs::Fs
///
/// let fs = Fs::init().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap();
/// ```
pub struct Archive {
id: ArchiveID,
handle: u64,
}
/// A reference to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written to depending
/// on what options It was opened with.
///
/// Files are automatically closed when they go out of scope.
pub struct File {
handle: u32,
offset: u64,
}
/// Metadata information about a file.
///
/// This structure is returned from the [`metadata`] function and
/// represents known metadata about a file.
///
/// [`metadata`]: fn.metadata.html
pub struct Metadata {
attributes: u32,
size: u64,
}
/// Options and flags which can be used to configure how a [`File`] is opened.
/// This builder exposes the ability to configure how a `File` is opened
/// and what operations are permitted on the open file. The [`File::open`]
/// and [`File::create`] methods are aliases for commonly used options
/// using this builder.
///
/// [`File`]: struct.File.html
/// [`File::open`]: struct.File.html#method.open
/// [`File::create`]: struct.File.html#method.create
///
/// Generally speaking, when using `OpenOptions`, you'll first call [`new()`],
/// then chain calls to methods to set each option, then call [`open()`],
/// passing the path of the file you're trying to open.
///
/// It is required to also pass a reference to the [`Archive`] that the
/// file lives in.
///
/// [`new()`]: struct.OpenOptions.html#method.new
/// [`open()`]: struct.OpenOptions.html#method.open
/// [`Archive`]: struct.Archive.html
///
/// # Examples
///
/// Opening a file to read:
///
/// ```no_run
/// use ctru::services::fs::OpenOptions;
///
/// let fs = Fs::init().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap();
/// let file = OpenOptions::new()
/// .read(true)
/// .archive(&sdmc_archive)
/// .open("foo.txt")
/// .unwrap();
/// ```
///
/// Opening a file for both reading and writing, as well as creating it if it
/// doesn't exist:
///
/// ```no_run
/// use ctru::services::fs::OpenOptions;
///
/// let fs = Fs::init().unwrap();
/// let sdmc_archive = fs.sdmc().unwrap();
/// let file = OpenOptions::new()
/// .read(true)
/// .write(true)
/// .create(true)
/// .archive(&sdmc_archive)
/// .open("foo.txt")
/// .unwrap();
/// ```
#[derive(Clone)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
arch_handle: u64,
}
/// Iterator over the entries in a directory.
///
/// This iterator is returned from the [`read_dir`] function of this module and
/// will yield instances of `Result<DirEntry, i32>`. Through a [`DirEntry`]
/// information like the entry's path and possibly other metadata can be
/// learned.
///
/// [`read_dir`]: fn.read_dir.html
/// [`DirEntry`]: struct.DirEntry.html
///
/// # Errors
///
/// This Result will return Err if there's some sort of intermittent IO error
/// during iteration.
pub struct ReadDir<'a> {
handle: Dir,
root: Arc<PathBuf>,
arch: &'a Archive,
}
/// Entries returned by the [`ReadDir`] iterator.
///
/// [`ReadDir`]: struct.ReadDir.html
///
/// An instance of `DirEntry` represents an entry inside of a directory on the
/// filesystem. Each entry can be inspected via methods to learn about the full
/// path or possibly other metadata.
pub struct DirEntry<'a> {
entry: FS_DirectoryEntry,
root: Arc<PathBuf>,
arch: &'a Archive,
}
#[doc(hidden)]
struct Dir(u32);
#[doc(hidden)]
unsafe impl Send for Dir {}
#[doc(hidden)]
unsafe impl Sync for Dir {}
impl Fs {
/// Initializes the FS service.
///
/// # Errors
///
/// This function will return Err if there was an error initializing the
/// FS service, which in practice should never happen unless there is
/// an error in the execution environment (i.e. the homebrew launcher
/// somehow fails to provide fs:USER permissions)
///
/// ctrulib services are reference counted, so this function may be called
/// as many times as desired and the service will not exit until all
/// instances of Fs drop out of scope.
pub fn init() -> Result<Fs, i32> {
unsafe {
let r = fsInit();
if r < 0 {
Err(r)
} else {
Ok(Fs { pd: PhantomData })
}
}
}
/// Returns a handle to the SDMC (memory card) Archive.
pub fn sdmc(&self) -> Result<Archive, i32> {
unsafe {
let mut handle = 0;
let id = ArchiveID::Sdmc;
let path = fsMakePath(PathType::Empty.into(), ptr::null() as _);
let r = FSUSER_OpenArchive(&mut handle, id.into(), path);
if r < 0 {
Err(r)
} else {
Ok(Archive {
handle: handle,
id: id,
})
}
}
}
}
impl Archive {
/// Retrieves an Archive's [`ArchiveID`]
///
/// [`ArchiveID`]: enum.ArchiveID.html
pub fn get_id(&self) -> ArchiveID {
self.id
}
}
impl File {
/// Attempts to open a file in read-only mode.
///
/// See the [`OpenOptions::open`] method for more details.
///
/// # Errors
///
/// This function will return an error if `path` does not already exit.
/// Other errors may also be returned accoridng to [`OpenOptions::open`]
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// # Examples
///
/// ```no_run
/// use ctru::services::fs::{Fs, File};
///
/// let fs = Fs::init().unwrap()
/// let sdmc_archive = fs.sdmc().unwrap()
/// let mut f = File::open(&sdmc_archive, "/foo.txt").unwrap();
/// ```
pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> {
OpenOptions::new().read(true).archive(arch).open(path.as_ref())
}
/// Opens a file in write-only mode.
///
/// This function will create a file if it does not exist.
///
/// See the [`OpenOptions::create`] method for more details.
///
/// # Errors
///
/// This function will return an error if `path` does not already exit.
/// Other errors may also be returned accoridng to [`OpenOptions::create`]
///
/// [`OpenOptions::create`]: struct.OpenOptions.html#method.create
///
/// # Examples
///
/// ```no_run
/// use ctru::services::fs::{Fs, File};
///
/// let fs = Fs::init().unwrap()
/// let sdmc_archive = fs.sdmc().unwrap()
/// let mut f = File::create(&sdmc_archive, "/foo.txt").unwrap();
/// ```
pub fn create<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<File, i32> {
OpenOptions::new().write(true).create(true).archive(arch).open(path.as_ref())
}
/// Truncates or extends the underlying file, updating the size of this file to become size.
///
/// If the size is less than the current file's size, then the file will be shrunk. If it is
/// greater than the current file's size, then the file will be extended to size and have all
/// of the intermediate data filled in with 0s.
pub fn set_len(&mut self, size: u64) -> Result<(), i32> {
unsafe {
let r = FSFILE_SetSize(self.handle, size);
if r < 0 {
Err(r)
} else {
Ok(())
}
}
}
/// Queries metadata about the underlying file.
pub fn metadata(&self) -> Result<Metadata, i32> {
// The only metadata we have for files right now is file size.
// This is likely to change in the future.
unsafe {
let mut size = 0;
let r = FSFILE_GetSize(self.handle, &mut size);
if r < 0 {
Err(r)
} else {
Ok(Metadata { attributes: 0, size: size })
}
}
}
/// Pull some bytes from the file into the specified buffer, returning
/// how many bytes were read.
///
/// This function will become private when std::io support is ported
/// to this library.
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, i32> {
unsafe {
let mut n_read = 0;
let r = FSFILE_Read(
self.handle,
&mut n_read,
self.offset,
buf.as_mut_ptr() as _,
buf.len() as u32
);
self.offset += n_read as u64;
if r < 0 {
Err(r)
} else {
Ok(n_read as usize)
}
}
}
/// Read all bytes until EOF in this source, placing them into buf.
///
/// This function will become private when std::io support is ported
/// to this library.
pub fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, i32> {
unsafe {
read_to_end_uninitialized(self, buf)
}
}
/// Write a buffer into this object, returning how many bytes were written.
///
/// This function will become private when std::io support is ported
/// to this library.
pub fn write(&mut self, buf: &[u8]) -> Result<usize, i32> {
unsafe {
let mut n_written = 0;
let r = FSFILE_Write(
self.handle,
&mut n_written,
self.offset,
buf.as_ptr() as _,
buf.len() as u32,
FS_WRITE_UPDATE_TIME
);
self.offset += n_written as u64;
if r < 0 {
Err(r)
} else {
Ok(n_written as usize)
}
}
}
}
impl Metadata {
/// Returns whether this metadata is for a directory.
pub fn is_dir(&self) -> bool {
self.attributes == self.attributes | FS_ATTRIBUTE_DIRECTORY
}
/// Returns whether this metadata is for a regular file.
pub fn is_file(&self) -> bool {
!self.is_dir()
}
/// Returns the size, in bytes, this metadata is for.
///
/// Directories return size = 0.
pub fn len(&self) -> u64 {
self.size
}
}
impl OpenOptions {
/// Creates a blank set of options ready for configuration.
///
/// All options are initially set to `false`
pub fn new() -> OpenOptions {
OpenOptions {
read: false,
write: false,
append: false,
truncate: false,
create: false,
arch_handle: 0,
}
}
/// Sets the option for read access.
///
/// This option, when true, will indicate that the file should be
/// `read`-able if opened.
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
self.read = read;
self
}
/// Sets the option for write access.
///
/// This option, when true, will indicate that the file should be
/// `write`-able if opened.
///
/// If the file already exists, any write calls on it will overwrite
/// its contents, without truncating it.
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
self.write = write;
self
}
/// Sets the option for the append mode.
///
/// This option, when true, means that writes will append to a file instead
/// of overwriting previous contents. Note that setting .write(true).append(true)
/// has the same effect as setting only .append(true).
///
/// If both truncate and append are set to true, the file will simply be truncated
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
self.append = append;
self
}
/// Sets the option for truncating a previous file.
///
/// If a file is successfully opened with this option set it will truncate
/// the file to 0 length if it already exists.
///
/// The file must be opened with write access for truncate to work.
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
self.truncate = truncate;
self
}
/// Sets the option for creating a new file.
///
/// This option indicates whether a new file will be created
/// if the file does not yet already
/// exist.
///
/// In order for the file to be created, write access must also be used.
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
self.create = create;
self
}
/// Sets which archive the file is to be opened in.
///
/// Failing to pass in an archive will result in the file failing to open.
pub fn archive(&mut self, archive: &Archive) -> &mut OpenOptions {
self.arch_handle = archive.handle;
self
}
/// Opens a file at `path` with the options specified by `self`
///
/// # Errors
///
/// This function will return an error under a number of different
/// circumstances, including but not limited to:
///
/// * Opening a file that doesn't exist without setting `create`.
/// * Attempting to open a file without passing an [`Archive`] reference
/// to the `archive` method.
/// * Filesystem-level errors (full disk, etc).
/// * Invalid combinations of open options.
///
/// [`Archive`]: struct.Archive.html
pub fn open<P: AsRef<Path>>(&self, path: P) -> Result<File, i32> {
self._open(path.as_ref(), self.get_open_flags())
}
fn _open(&self, path: &Path, flags: u32) -> Result<File, i32> {
unsafe {
let mut file_handle = 0;
let path = to_utf16(path);
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_OpenFile(&mut file_handle, self.arch_handle, fs_path, flags, 0);
if r < 0 {
return Err(r);
}
let mut file = File { handle: file_handle, offset: 0 };
if self.append {
let metadata = try!(file.metadata());
file.offset = metadata.len();
}
// set the offset to 0 just in case both append and truncate were
// set to true
if self.truncate {
try!(file.set_len(0));
file.offset = 0;
}
Ok(file)
}
}
fn get_open_flags(&self) -> u32 {
match (self.read, self.write || self.append, self.create) {
(true, false, false) => FS_OPEN_READ,
(false, true, false) => FS_OPEN_WRITE,
(false, true, true) => FS_OPEN_WRITE | FS_OPEN_CREATE,
(true, false, true) => FS_OPEN_READ | FS_OPEN_CREATE,
(true, true, false) => FS_OPEN_READ | FS_OPEN_WRITE,
(true, true, true) => FS_OPEN_READ | FS_OPEN_WRITE | FS_OPEN_CREATE,
_ => 0, //failure case
}
}
}
impl<'a> Iterator for ReadDir<'a> {
type Item = Result<DirEntry<'a>, i32>;
fn next(&mut self) -> Option<Result<DirEntry<'a>, i32>> {
unsafe {
let mut ret = DirEntry {
entry: mem::zeroed(),
root: self.root.clone(),
arch: self.arch,
};
let mut entries_read = 0;
let entry_count = 1;
let r = FSDIR_Read(self.handle.0, &mut entries_read, entry_count, &mut ret.entry);
if r < 0 {
return Some(Err(r))
}
if entries_read != entry_count {
return None
}
Some(Ok(ret))
}
}
}
impl<'a> DirEntry<'a> {
/// Returns the full path to the file that this entry represents.
///
/// The full path is created by joining the original path to `read_dir`
/// with the filename of this entry.
pub fn path(&self) -> PathBuf {
self.root.join(&self.file_name())
}
/// Return the metadata for the file that this entry points at.
pub fn metadata(&self) -> Result<Metadata, i32> {
metadata(self.arch, self.path())
}
/// Returns the bare file name of this directory entry without any other leading path
/// component.
pub fn file_name(&self) -> OsString {
let filename = truncate_utf16_at_nul(&self.entry.name);
OsString::from_wide(filename)
}
}
/// Creates a new, empty directory at the provided path
///
/// # Errors
///
/// This function will return an error in the following situations,
/// but is not limited to just these cases:
///
/// * User lacks permissions to create directory at `path`
pub fn create_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_CreateDirectory(arch.handle, fs_path, FS_ATTRIBUTE_DIRECTORY);
if r < 0 {
Err(r)
} else {
Ok(())
}
}
}
/// Recursively create a directory and all of its parent components if they are missing.
///
/// # Errors
///
/// This function will return an error in the following situations,
/// but is not limited to just these cases:
///
/// * If any directory in the path specified by `path` does not already exist
/// and it could not be created otherwise.
pub fn create_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
let path = path.as_ref();
let mut dir = PathBuf::new();
let mut result = Ok(());
for component in path.components() {
let component = component.as_os_str();
dir.push(component);
result = create_dir(arch, &dir);
}
result
}
/// Given a path, query the file system to get information about a file, directory, etc
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),
}
}
/// Removes an existing, empty directory.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not limited to just
/// these cases:
///
/// * The user lacks permissions to remove the directory at the provided path.
/// * The directory isn't empty.
pub fn remove_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_DeleteDirectory(arch.handle, fs_path);
if r < 0 {
Err(r)
} else {
Ok(())
}
}
}
/// Removes a directory at this path, after removing all its contents. Use carefully!
///
/// # Errors
///
/// see `file::remove_file` and `fs::remove_dir`
pub fn remove_dir_all<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_DeleteDirectoryRecursively(arch.handle, fs_path);
if r < 0 {
Err(r)
} else {
Ok(())
}
}
}
/// Returns an iterator over the entries within a directory.
///
/// The iterator will yield instances of Result<DirEntry, i32>. New errors
/// may be encountered after an iterator is initially constructed.
///
/// This function will return an error in the following situations, but is not limited to just
/// these cases:
///
/// * The provided path doesn't exist.
/// * The process lacks permissions to view the contents.
/// * The path points at a non-directory file.
pub fn read_dir<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<ReadDir, i32> {
unsafe {
let mut handle = 0;
let root = Arc::new(path.as_ref().to_path_buf());
let path = to_utf16(path.as_ref());
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_OpenDirectory(&mut handle, arch.handle, fs_path);
if r < 0 {
Err(r)
} else {
Ok(ReadDir { handle: Dir(handle), root: root, arch: arch})
}
}
}
/// Removes a file from the filesystem.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not limited to just
/// these cases:
///
/// * path points to a directory.
/// * The user lacks permissions to remove the file.
pub fn remove_file<P: AsRef<Path>>(arch: &Archive, path: P) -> Result<(), i32> {
unsafe {
let path = to_utf16(path.as_ref());
let fs_path = fsMakePath(PathType::UTF16.into(), path.as_ptr() as _);
let r = FSUSER_DeleteFile(arch.handle, fs_path);
if r < 0 {
Err(r)
} else {
Ok(())
}
}
}
/// Rename a file or directory to a new name, replacing the original file
/// if to already exists.
///
/// # Errors
///
/// This function will return an error in the following situations, but is not limited to just
/// these cases:
///
/// * from does not exist.
/// * The user lacks permissions to view contents.
pub fn rename<P, Q>(arch: &Archive, from: P, to: Q) -> Result<(), i32>
where P: AsRef<Path>,
Q: AsRef<Path> {
unsafe {
let from = to_utf16(from.as_ref());
let to = to_utf16(to.as_ref());
let fs_from = fsMakePath(PathType::UTF16.into(), from.as_ptr() as _);
let fs_to = fsMakePath(PathType::UTF16.into(), to.as_ptr() as _);
let r = FSUSER_RenameFile(arch.handle, fs_from, arch.handle, fs_to);
if r == 0 {
return Ok(())
}
let r = FSUSER_RenameDirectory(arch.handle, fs_from, arch.handle, fs_to);
if r == 0 {
return Ok(())
}
Err((r))
}
}
// TODO: Determine if we should check UTF-16 paths for interior NULs
fn to_utf16(path: &Path) -> Vec<u16> {
path.as_os_str().encode_wide().collect::<Vec<_>>()
}
// Adapted from sys/windows/fs.rs in libstd
fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
match v.iter().position(|c| *c == 0) {
// don't include the 0
Some(i) => &v[..i],
None => v
}
}
// Adapted from sys/common/io.rs in libstd
unsafe fn read_to_end_uninitialized(f: &mut File, buf: &mut Vec<u8>) -> Result<usize, i32> {
let start_len = buf.len();
buf.reserve(16);
// Always try to read into the empty space of the vector (from the length to the capacity).
// If the vector ever fills up then we reserve an extra byte which should trigger the normal
// reallocation routines for the vector, which will likely double the size.
//
// This function is similar to the read_to_end function in std::io, but the logic about
// reservations and slicing is different enough that this is duplicated here.
loop {
if buf.len() == buf.capacity() {
buf.reserve(1);
}
let buf_slice = slice::from_raw_parts_mut(buf.as_mut_ptr().offset(buf.len() as isize),
buf.capacity() - buf.len());
match f.read(buf_slice) {
Ok(0) => { return Ok(buf.len() - start_len); }
Ok(n) => { let len = buf.len() + n; buf.set_len(len); },
Err(e) => { return Err(e); }
}
}
}
impl Drop for Fs {
fn drop(&mut self) {
unsafe {
fsExit();
}
}
}
impl Drop for Archive {
fn drop(&mut self) {
unsafe {
FSUSER_CloseArchive(self.handle);
}
}
}
impl Drop for File {
fn drop(&mut self) {
unsafe {
FSFILE_Close(self.handle);
}
}
}
impl Drop for Dir {
fn drop(&mut self) {
unsafe {
FSDIR_Close(self.0);
}
}
}
impl From<PathType> for FS_PathType {
fn from(p: PathType) -> Self {
use self::PathType::*;
use libctru::services::fs::FS_PathType::*;
match p {
Invalid => PATH_INVALID,
Empty => PATH_EMPTY,
Binary => PATH_BINARY,
ASCII => PATH_ASCII,
UTF16 => PATH_UTF16,
}
}
}
impl From<ArchiveID> for FS_ArchiveID {
fn from(a: ArchiveID) -> Self {
use self::ArchiveID::*;
use libctru::services::fs::FS_ArchiveID::*;
match a {
RomFS => ARCHIVE_ROMFS,
Savedata => ARCHIVE_SAVEDATA,
Extdata => ARCHIVE_EXTDATA,
SharedExtdata => ARCHIVE_SHARED_EXTDATA,
SystemSavedata => ARCHIVE_SYSTEM_SAVEDATA,
Sdmc => ARCHIVE_SDMC,
SdmcWriteOnly => ARCHIVE_SDMC_WRITE_ONLY,
BossExtdata => ARCHIVE_BOSS_EXTDATA,
CardSpiFS => ARCHIVE_CARD_SPIFS,
ExtDataAndBossExtdata => ARCHIVE_EXTDATA_AND_BOSS_EXTDATA,
SystemSaveData2 => ARCHIVE_SYSTEM_SAVEDATA2,
NandRW => ARCHIVE_NAND_RW,
NandRO => ARCHIVE_NAND_RO,
NandROWriteAccess => ARCHIVE_NAND_RO_WRITE_ACCESS,
SaveDataAndContent => ARCHIVE_SAVEDATA_AND_CONTENT,
SaveDataAndContent2 => ARCHIVE_SAVEDATA_AND_CONTENT2,
NandCtrFS => ARCHIVE_NAND_CTR_FS,
TwlPhoto => ARCHIVE_TWL_PHOTO,
NandTwlFS => ARCHIVE_NAND_TWL_FS,
GameCardSavedata => ARCHIVE_GAMECARD_SAVEDATA,
UserSavedata => ARCHIVE_USER_SAVEDATA,
DemoSavedata => ARCHIVE_DEMO_SAVEDATA,
}
}
}

1
src/services/mod.rs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
pub mod apt;
pub mod fs;
pub mod hid;
pub mod gspgpu;

25
src/sys/mod.rs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInner<Inner: ?Sized> {
fn as_inner(&self) -> &Inner;
}
/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInnerMut<Inner: ?Sized> {
fn as_inner_mut(&mut self) -> &mut Inner;
}
/// A trait for extracting representations from std types
#[doc(hidden)]
pub trait IntoInner<Inner> {
fn into_inner(self) -> Inner;
}
/// A trait for creating std types from internal representations
#[doc(hidden)]
pub trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self;
}
pub mod wtf8;

1206
src/sys/wtf8.rs

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save