|
1 use std::ffi::CString; |
|
2 use std::io::{ Read, Write, Seek, SeekFrom, Result }; |
|
3 use std::mem; |
|
4 use libc::{ c_int, c_char, c_void }; |
|
5 use primitives::*; |
|
6 use super::{ PhysFSContext }; |
|
7 use super::util::physfs_error_as_io_error; |
|
8 |
|
9 #[link(name = "physfs")] |
|
10 extern { |
|
11 // valid filehandle on success, NULL on failure |
|
12 fn PHYSFS_openAppend(filename: *const c_char) -> *const RawFile; |
|
13 fn PHYSFS_openRead(filename: *const c_char) -> *const RawFile; |
|
14 fn PHYSFS_openWrite(filename: *const c_char) -> *const RawFile; |
|
15 |
|
16 // nonzero on success, 0 on failure (and the handle stays open) |
|
17 // The docs make it sound like failure is rare. |
|
18 fn PHYSFS_close(file: *const RawFile) -> c_int; |
|
19 |
|
20 // Number of bytes read on success, -1 on failure. |
|
21 fn PHYSFS_read(file: *const RawFile, buffer: *mut c_void, |
|
22 obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64; |
|
23 |
|
24 // Number of bytes written on success, -1 on failure. |
|
25 fn PHYSFS_write(file: *const RawFile, buffer: *const c_void, |
|
26 obj_size: PHYSFS_uint32, obj_count: PHYSFS_uint32) -> PHYSFS_sint64; |
|
27 |
|
28 // Flush buffered file; no-op for unbuffered files. |
|
29 fn PHYSFS_flush(file: *const RawFile) -> c_int; |
|
30 |
|
31 // Seek to position in file; nonzero on succss, zero on error. |
|
32 fn PHYSFS_seek(file: *const RawFile, pos: PHYSFS_uint64) -> c_int; |
|
33 |
|
34 // Current position in file, -1 on failure. |
|
35 fn PHYSFS_tell(file: *const RawFile) -> PHYSFS_sint64; |
|
36 |
|
37 // nonzero if EOF, zero if not. |
|
38 fn PHYSFS_eof(file: *const RawFile) -> c_int; |
|
39 |
|
40 // Determine file size; returns -1 if impossible |
|
41 fn PHYSFS_fileLength(file: *const RawFile) -> PHYSFS_sint64; |
|
42 } |
|
43 |
|
44 /// Possible ways to open a file. |
|
45 #[derive(Copy, Clone)] |
|
46 pub enum Mode { |
|
47 /// Append to the end of the file. |
|
48 Append, |
|
49 /// Read from the file. |
|
50 Read, |
|
51 /// Write to the file, overwriting previous data. |
|
52 Write, |
|
53 } |
|
54 |
|
55 /// A wrapper for the PHYSFS_File type. |
|
56 #[repr(C)] |
|
57 struct RawFile { |
|
58 opaque: *const c_void, |
|
59 } |
|
60 |
|
61 /// A file handle. |
|
62 #[allow(dead_code)] |
|
63 pub struct File<'f> { |
|
64 raw: *const RawFile, |
|
65 mode: Mode, |
|
66 context: &'f PhysFSContext, |
|
67 } |
|
68 |
|
69 impl<'f> File<'f> { |
|
70 /// Opens a file with a specific mode. |
|
71 pub fn open<'g>(context: &'g PhysFSContext, filename: String, mode: Mode) -> Result<File<'g>> { |
|
72 let c_filename = try!(CString::new(filename)); |
|
73 let raw = unsafe { match mode { |
|
74 Mode::Append => PHYSFS_openAppend(c_filename.as_ptr()), |
|
75 Mode::Read => PHYSFS_openRead(c_filename.as_ptr()), |
|
76 Mode::Write => PHYSFS_openWrite(c_filename.as_ptr()) |
|
77 }}; |
|
78 |
|
79 if raw.is_null() { |
|
80 Err(physfs_error_as_io_error()) |
|
81 } |
|
82 else { |
|
83 Ok(File{raw: raw, mode: mode, context: context}) |
|
84 } |
|
85 } |
|
86 |
|
87 /// Closes a file handle. |
|
88 fn close(&self) -> Result<()> { |
|
89 match unsafe { |
|
90 PHYSFS_close(self.raw) |
|
91 } { |
|
92 0 => Err(physfs_error_as_io_error()), |
|
93 _ => Ok(()) |
|
94 } |
|
95 } |
|
96 |
|
97 /// Checks whether eof is reached or not. |
|
98 pub fn eof(&self) -> bool { |
|
99 let ret = unsafe { |
|
100 PHYSFS_eof(self.raw) |
|
101 }; |
|
102 |
|
103 ret != 0 |
|
104 } |
|
105 |
|
106 /// Determine length of file, if possible |
|
107 pub fn len(&self) -> Result<u64> { |
|
108 let len = unsafe { PHYSFS_fileLength(self.raw) }; |
|
109 |
|
110 if len >= 0 { |
|
111 Ok(len as u64) |
|
112 } else { |
|
113 Err(physfs_error_as_io_error()) |
|
114 } |
|
115 } |
|
116 |
|
117 /// Determines current position within a file |
|
118 pub fn tell(&self) -> Result<u64> { |
|
119 let ret = unsafe { |
|
120 PHYSFS_tell(self.raw) |
|
121 }; |
|
122 |
|
123 match ret { |
|
124 -1 => Err(physfs_error_as_io_error()), |
|
125 _ => Ok(ret as u64) |
|
126 } |
|
127 } |
|
128 } |
|
129 |
|
130 impl<'f> Read for File<'f> { |
|
131 /// Reads from a file |
|
132 fn read(&mut self, buf: &mut [u8]) -> Result<usize> { |
|
133 let ret = unsafe { |
|
134 PHYSFS_read( |
|
135 self.raw, |
|
136 buf.as_ptr() as *mut c_void, |
|
137 mem::size_of::<u8>() as PHYSFS_uint32, |
|
138 buf.len() as PHYSFS_uint32 |
|
139 ) |
|
140 }; |
|
141 |
|
142 match ret { |
|
143 -1 => Err(physfs_error_as_io_error()), |
|
144 _ => Ok(ret as usize) |
|
145 } |
|
146 } |
|
147 } |
|
148 |
|
149 impl<'f> Write for File<'f> { |
|
150 /// Writes to a file. |
|
151 /// This code performs no safety checks to ensure |
|
152 /// that the buffer is the correct length. |
|
153 fn write(&mut self, buf: &[u8]) -> Result<usize> { |
|
154 let ret = unsafe { |
|
155 PHYSFS_write( |
|
156 self.raw, |
|
157 buf.as_ptr() as *const c_void, |
|
158 mem::size_of::<u8>() as PHYSFS_uint32, |
|
159 buf.len() as PHYSFS_uint32 |
|
160 ) |
|
161 }; |
|
162 |
|
163 match ret { |
|
164 -1 => Err(physfs_error_as_io_error()), |
|
165 _ => Ok(ret as usize) |
|
166 } |
|
167 } |
|
168 |
|
169 /// Flushes a file if buffered; no-op if unbuffered. |
|
170 fn flush(&mut self) -> Result<()> { |
|
171 let ret = unsafe { |
|
172 PHYSFS_flush(self.raw) |
|
173 }; |
|
174 |
|
175 match ret { |
|
176 0 => Err(physfs_error_as_io_error()), |
|
177 _ => Ok(()) |
|
178 } |
|
179 } |
|
180 } |
|
181 |
|
182 impl<'f> Seek for File<'f> { |
|
183 /// Seek to a new position within a file |
|
184 fn seek(&mut self, pos: SeekFrom) -> Result<u64> { |
|
185 let seek_pos = match pos { |
|
186 SeekFrom::Start(n) => n as i64, |
|
187 SeekFrom::End(n) => { |
|
188 let len = try!(self.len()); |
|
189 n + len as i64 |
|
190 } |
|
191 SeekFrom::Current(n) => { |
|
192 let curr_pos = try!(self.tell()); |
|
193 n + curr_pos as i64 |
|
194 } |
|
195 }; |
|
196 |
|
197 let result = unsafe { |
|
198 PHYSFS_seek( |
|
199 self.raw, |
|
200 seek_pos as PHYSFS_uint64 |
|
201 ) |
|
202 }; |
|
203 |
|
204 if result == -1 { |
|
205 return Err(physfs_error_as_io_error()); |
|
206 } |
|
207 |
|
208 self.tell() |
|
209 } |
|
210 } |
|
211 |
|
212 impl<'f> Drop for File<'f> { |
|
213 fn drop(&mut self) { |
|
214 let _ = self.close(); |
|
215 } |
|
216 } |