|
1 use std::ffi::{ CString, CStr, OsStr }; |
|
2 use std::io::Result; |
|
3 use std::sync::{ Mutex }; |
|
4 use libc::{ c_int, c_char }; |
|
5 |
|
6 /// Keep track of the number of global contexts. |
|
7 static mut NUM_CONTEXTS: usize = 0; |
|
8 |
|
9 /// Utility |
|
10 mod util; |
|
11 /// File operations |
|
12 pub mod file; |
|
13 |
|
14 #[link(name = "physfs")] |
|
15 extern { |
|
16 // nonzero on success, zero on error. |
|
17 fn PHYSFS_init(arg0: *const c_char) -> c_int; |
|
18 // nonzero if initialized, zero if not. |
|
19 fn PHYSFS_isInit() -> c_int; |
|
20 // nonzero if success, zero if error. |
|
21 fn PHYSFS_deinit() -> c_int; |
|
22 // string if success, NULL if error. |
|
23 fn PHYSFS_getLastError() -> *const c_char; |
|
24 // nonzero if success, zero if error |
|
25 fn PHYSFS_mount(new_dir: *const c_char, mount_point: *const c_char, append_to_path: c_int) -> c_int; |
|
26 // nonzero if success, zero if error. |
|
27 fn PHYSFS_setWriteDir(write_dir: *const c_char) -> c_int; |
|
28 // nonzero on success, zero on error. |
|
29 fn PHYSFS_mkdir(dir_name: *const c_char) -> c_int; |
|
30 // Checks if a given path exists; returns nonzero if true |
|
31 fn PHYSFS_exists(path: *const c_char) -> c_int; |
|
32 // Checks if a given path is a directory; returns nonzero if true |
|
33 fn PHYSFS_isDirectory(path: *const c_char) -> c_int; |
|
34 } |
|
35 |
|
36 /// The access point for PhysFS function calls. |
|
37 pub struct PhysFSContext; |
|
38 |
|
39 unsafe impl Send for PhysFSContext {} |
|
40 |
|
41 impl PhysFSContext { |
|
42 /// Creates a new PhysFS context. |
|
43 pub fn new() -> Result<Self> { |
|
44 let con = PhysFSContext; |
|
45 match PhysFSContext::init() { |
|
46 Err(e) => Err(e), |
|
47 _ => { |
|
48 // Everything's gone right so far |
|
49 // now, increment the instance counter |
|
50 println!("Inc"); |
|
51 unsafe { |
|
52 NUM_CONTEXTS += 1; |
|
53 } |
|
54 // and return the newly created context |
|
55 Ok(con) |
|
56 } |
|
57 } |
|
58 } |
|
59 |
|
60 /// initializes the PhysFS library. |
|
61 fn init() -> Result<()> { |
|
62 // Initializing multiple times throws an error. So let's not! |
|
63 if PhysFSContext::is_init() { return Ok(()); } |
|
64 |
|
65 let mut args = ::std::env::args(); |
|
66 let default_arg0 = "".to_string(); |
|
67 let arg0 = args.next().unwrap_or(default_arg0); |
|
68 let c_arg0 = try!(CString::new(arg0)); |
|
69 let ret = unsafe { PHYSFS_init(c_arg0.as_ptr()) }; |
|
70 |
|
71 match ret { |
|
72 0 => Err(util::physfs_error_as_io_error()), |
|
73 _ => Ok(()) |
|
74 } |
|
75 } |
|
76 |
|
77 /// Checks if PhysFS is initialized |
|
78 pub fn is_init() -> bool { |
|
79 unsafe { PHYSFS_isInit() != 0 } |
|
80 } |
|
81 |
|
82 /// De-initializes PhysFS. It is recommended to close |
|
83 /// all file handles manually before calling this. |
|
84 fn de_init() { |
|
85 // de_init'ing more than once can cause a double-free -- do not want. |
|
86 if !PhysFSContext::is_init() { return } |
|
87 unsafe { |
|
88 PHYSFS_deinit(); |
|
89 } |
|
90 } |
|
91 /// Adds an archive or directory to the search path. |
|
92 /// mount_point is the location in the tree to mount it to. |
|
93 pub fn mount<P>(&self, new_dir: P, mount_point: String, append_to_path: bool) -> Result<()> |
|
94 where P: AsRef<OsStr> |
|
95 { |
|
96 let c_new_dir = CString::new(new_dir.as_ref().to_string_lossy().as_bytes()).unwrap(); |
|
97 let c_mount_point = try!(CString::new(mount_point)); |
|
98 match unsafe { |
|
99 PHYSFS_mount( |
|
100 c_new_dir.as_c_str().as_ptr(), |
|
101 c_mount_point.as_ptr(), |
|
102 append_to_path as c_int |
|
103 ) |
|
104 } { |
|
105 0 => Err(util::physfs_error_as_io_error()), |
|
106 _ => Ok(()) |
|
107 } |
|
108 } |
|
109 |
|
110 /// Gets the last error message in a human-readable format |
|
111 /// This message may be localized, so do not expect it to |
|
112 /// match a specific string of characters. |
|
113 pub fn get_last_error() -> String { |
|
114 let ptr: *const c_char = unsafe { |
|
115 PHYSFS_getLastError() |
|
116 }; |
|
117 if ptr.is_null() { |
|
118 return "".to_string() |
|
119 } |
|
120 |
|
121 let buf = unsafe { CStr::from_ptr(ptr).to_bytes().to_vec() }; |
|
122 |
|
123 String::from_utf8(buf).unwrap() |
|
124 } |
|
125 |
|
126 /// Sets a new write directory. |
|
127 /// This method will fail if the current write dir |
|
128 /// still has open files in it. |
|
129 pub fn set_write_dir<P>(&self, write_dir: P) -> Result<()> |
|
130 where P: AsRef<OsStr> |
|
131 { |
|
132 let write_dir = CStr::from_bytes_with_nul(write_dir.as_ref().to_str().unwrap().as_bytes()).unwrap(); |
|
133 let ret = unsafe { |
|
134 PHYSFS_setWriteDir(write_dir.as_ptr()) |
|
135 }; |
|
136 |
|
137 match ret { |
|
138 0 => Err(util::physfs_error_as_io_error()), |
|
139 _ => Ok(()) |
|
140 } |
|
141 } |
|
142 |
|
143 /// Creates a new dir relative to the write_dir. |
|
144 pub fn mkdir(&self, dir_name: &str) -> Result<()> { |
|
145 let c_dir_name = try!(CString::new(dir_name)); |
|
146 let ret = unsafe { |
|
147 PHYSFS_mkdir(c_dir_name.as_ptr()) |
|
148 }; |
|
149 |
|
150 match ret { |
|
151 0 => Err(util::physfs_error_as_io_error()), |
|
152 _ => Ok(()) |
|
153 } |
|
154 } |
|
155 |
|
156 /// Checks if given path exists |
|
157 pub fn exists(&self, path: &str) -> Result<()> { |
|
158 let c_path = try!(CString::new(path)); |
|
159 let ret = unsafe { PHYSFS_exists(c_path.as_ptr()) }; |
|
160 |
|
161 match ret { |
|
162 0 => Err(util::physfs_error_as_io_error()), |
|
163 _ => Ok(()) |
|
164 } |
|
165 } |
|
166 |
|
167 /// Checks if given path is a directory |
|
168 pub fn is_directory(&self, path: &str) -> Result<()> { |
|
169 let c_path = try!(CString::new(path)); |
|
170 let ret = unsafe { PHYSFS_isDirectory(c_path.as_ptr()) }; |
|
171 |
|
172 match ret { |
|
173 0 => Err(util::physfs_error_as_io_error()), |
|
174 _ => Ok(()) |
|
175 } |
|
176 } |
|
177 } |
|
178 |
|
179 impl Drop for PhysFSContext { |
|
180 fn drop(&mut self) { |
|
181 // decrement NUM_CONTEXTS |
|
182 unsafe { |
|
183 NUM_CONTEXTS -= 1; |
|
184 } |
|
185 // and de_init if there aren't any contexts left. |
|
186 if unsafe { NUM_CONTEXTS == 0 } { |
|
187 PhysFSContext::de_init(); |
|
188 } |
|
189 } |
|
190 } |