Panda3D
virtualFileMountAndroidAsset.cxx
1 // Filename: virtualFileMountAndroidAsset.cxx
2 // Created by: rdb (21Jan13)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #ifdef ANDROID
16 
17 #include "virtualFileMountAndroidAsset.h"
18 #include "virtualFileSystem.h"
19 
20 #ifndef NDEBUG
21 #include <sys/stat.h>
22 #endif
23 
24 TypeHandle VirtualFileMountAndroidAsset::_type_handle;
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: VirtualFileMountAndroidAsset::Destructor
28 // Access: Public, Virtual
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 VirtualFileMountAndroidAsset::
32 ~VirtualFileMountAndroidAsset() {
33 }
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: VirtualFileMountAndroidAsset::get_fd
37 // Access: Public
38 // Description: Returns a file descriptor that can be used to read
39 // the asset if it was stored uncompressed and
40 // unencrypted. Returns a valid fd or -1.
41 ////////////////////////////////////////////////////////////////////
42 int VirtualFileMountAndroidAsset::
43 get_fd(const Filename &file, off_t *start, off_t *length) const {
44  AAsset* asset;
45  asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
46 
47  int fd = AAsset_openFileDescriptor(asset, start, length);
48  AAsset_close(asset);
49  return fd;
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: VirtualFileMountAndroidAsset::has_file
54 // Access: Public, Virtual
55 // Description: Returns true if the indicated file exists within the
56 // mount system.
57 ////////////////////////////////////////////////////////////////////
58 bool VirtualFileMountAndroidAsset::
59 has_file(const Filename &file) const {
60  return (file.empty() || /*is_directory(file) ||*/ is_regular_file(file));
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: VirtualFileMountAndroidAsset::is_directory
65 // Access: Public, Virtual
66 // Description: Returns true if the indicated file exists within the
67 // mount system and is a directory.
68 ////////////////////////////////////////////////////////////////////
69 bool VirtualFileMountAndroidAsset::
70 is_directory(const Filename &file) const {
71  // This is the only way - AAssetManager_openDir also works for files.
72  //AAssetDir *dir = AAssetManager_openDir(_asset_mgr, file.c_str());
73 
74  //express_cat.error() << "is_directory " << file << " - " << dir << "\n";
75 
76  //if (dir == NULL) {
77  // return false;
78  //}
79  //AAssetDir_close(dir);
80 
81  // openDir doesn't return NULL for ordinary files!
82  return !is_regular_file(file);
83 }
84 
85 ////////////////////////////////////////////////////////////////////
86 // Function: VirtualFileMountAndroidAsset::is_regular_file
87 // Access: Public, Virtual
88 // Description: Returns true if the indicated file exists within the
89 // mount system and is a regular file.
90 ////////////////////////////////////////////////////////////////////
91 bool VirtualFileMountAndroidAsset::
92 is_regular_file(const Filename &file) const {
93  // I'm afraid the only way to see if it exists is to try and open it.
94  AAsset* asset;
95  asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
96 
97  express_cat.error() << "is_regular_file " << file << " - " << asset << "\n";
98 
99  if (asset == NULL) {
100  return false;
101  }
102  AAsset_close(asset);
103  return true;
104 }
105 
106 ////////////////////////////////////////////////////////////////////
107 // Function: VirtualFileMountAndroidAsset::read_file
108 // Access: Public, Virtual
109 // Description: Fills up the indicated pvector with the contents of
110 // the file, if it is a regular file. Returns true on
111 // success, false otherwise.
112 ////////////////////////////////////////////////////////////////////
113 bool VirtualFileMountAndroidAsset::
114 read_file(const Filename &file, bool do_uncompress,
115  pvector<unsigned char> &result) const {
116  if (do_uncompress) {
117  // If the file is to be decompressed, we'd better just use the
118  // higher-level implementation, which includes support for
119  // on-the-fly decompression.
120  return VirtualFileMount::read_file(file, do_uncompress, result);
121  }
122 
123  // But if we're just reading a straight file, let's just read
124  // it here to avoid all of the streambuf nonsense.
125  result.clear();
126 
127  AAsset* asset;
128  asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_STREAMING);
129  if (asset == (AAsset *)NULL) {
130  express_cat.info()
131  << "Unable to read " << file << "\n";
132  return false;
133  }
134 
135  // Reserve enough space to hold the entire file.
136  off_t file_size = AAsset_getLength(asset);
137  if (file_size == 0) {
138  return true;
139  } else if (file_size > 0) {
140  result.reserve((size_t)file_size);
141  }
142 
143  static const size_t buffer_size = 4096;
144  char buffer[buffer_size];
145 
146  int count = AAsset_read(asset, buffer, buffer_size);
147  while (count > 0) {
148  thread_consider_yield();
149  result.insert(result.end(), buffer, buffer + count);
150  count = AAsset_read(asset, buffer, buffer_size);
151  }
152 
153  AAsset_close(asset);
154  return (count == 0);
155 }
156 
157 ////////////////////////////////////////////////////////////////////
158 // Function: VirtualFileMountAndroidAsset::open_read_file
159 // Access: Public, Virtual
160 // Description: Opens the file for reading, if it exists. Returns a
161 // newly allocated istream on success (which you should
162 // eventually delete when you are done reading).
163 // Returns NULL on failure.
164 ////////////////////////////////////////////////////////////////////
165 istream *VirtualFileMountAndroidAsset::
166 open_read_file(const Filename &file) const {
167  AAsset* asset;
168  asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
169  if (asset == (AAsset *)NULL) {
170  return NULL;
171  }
172 
173  AssetStream *stream = new AssetStream(asset);
174  return (istream *) stream;
175 }
176 
177 ////////////////////////////////////////////////////////////////////
178 // Function: VirtualFileMountAndroidAsset::get_file_size
179 // Access: Published, Virtual
180 // Description: Returns the current size on disk (or wherever it is)
181 // of the already-open file. Pass in the stream that
182 // was returned by open_read_file(); some
183 // implementations may require this stream to determine
184 // the size.
185 ////////////////////////////////////////////////////////////////////
186 streamsize VirtualFileMountAndroidAsset::
187 get_file_size(const Filename &file, istream *in) const {
188  // If it's already open, get the AAsset pointer from the streambuf.
189  const AssetStreamBuf *buf = (const AssetStreamBuf *) in->rdbuf();
190  off_t length = AAsset_getLength(buf->_asset);
191  return length;
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: VirtualFileMountAndroidAsset::get_file_size
196 // Access: Published, Virtual
197 // Description: Returns the current size on disk (or wherever it is)
198 // of the file before it has been opened.
199 ////////////////////////////////////////////////////////////////////
200 streamsize VirtualFileMountAndroidAsset::
201 get_file_size(const Filename &file) const {
202  AAsset* asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
203  off_t length = AAsset_getLength(asset);
204  AAsset_close(asset);
205  return length;
206 }
207 
208 ////////////////////////////////////////////////////////////////////
209 // Function: VirtualFileMountAndroidAsset::get_timestamp
210 // Access: Published, Virtual
211 // Description: Returns a time_t value that represents the time the
212 // file was last modified, to within whatever precision
213 // the operating system records this information (on a
214 // Windows95 system, for instance, this may only be
215 // accurate to within 2 seconds).
216 //
217 // If the timestamp cannot be determined, either because
218 // it is not supported by the operating system or
219 // because there is some error (such as file not found),
220 // returns 0.
221 ////////////////////////////////////////////////////////////////////
222 time_t VirtualFileMountAndroidAsset::
223 get_timestamp(const Filename &file) const {
224  // There's no obvious way to get a timestamp from an Android asset.
225  return 0;
226 }
227 
228 ////////////////////////////////////////////////////////////////////
229 // Function: VirtualFileMountAndroidAsset::get_system_info
230 // Access: Public, Virtual
231 // Description: Populates the SubfileInfo structure with the data
232 // representing where the file actually resides on disk,
233 // if this is knowable. Returns true if the file might
234 // reside on disk, and the info is populated, or false
235 // if it might not (or it is not known where the file
236 // resides), in which case the info is meaningless.
237 ////////////////////////////////////////////////////////////////////
238 bool VirtualFileMountAndroidAsset::
239 get_system_info(const Filename &file, SubfileInfo &info) {
240  off_t start, length;
241  int fd = get_fd(file, &start, &length);
242 
243 #ifndef NDEBUG
244  // Double-check that this fd actually points to the apk.
245  struct stat st1, st2;
246  nassertr(fstat(fd, &st1) == 0, false);
247  nassertr(stat(_apk_path.c_str(), &st2) == 0, false);
248  nassertr(st1.st_dev == st2.st_dev, false);
249  nassertr(st1.st_ino == st2.st_ino, false);
250 #endif
251 
252  // We don't actually need the file descriptor, so close it.
253  close(fd);
254 
255  info = SubfileInfo(_apk_path, start, length);
256  return true;
257 }
258 
259 ////////////////////////////////////////////////////////////////////
260 // Function: VirtualFileMountAndroidAsset::scan_directory
261 // Access: Public, Virtual
262 // Description: Fills the given vector up with the list of filenames
263 // that are local to this directory, if the filename is
264 // a directory. Returns true if successful, or false if
265 // the file is not a directory or cannot be read.
266 ////////////////////////////////////////////////////////////////////
267 bool VirtualFileMountAndroidAsset::
268 scan_directory(vector_string &contents, const Filename &dir) const {
269  AAssetDir *asset_dir = AAssetManager_openDir(_asset_mgr, dir.c_str());
270  if (asset_dir == NULL) {
271  return false;
272  }
273 
274  // Note: this returns the full path.
275  const char *fullpath = AAssetDir_getNextFileName(asset_dir);
276 
277  while (fullpath != NULL) {
278  express_cat.error() << fullpath << "\n"; // DEBUG
279  // Determine the basename and add it to the vector.
280  Filename fname (fullpath);
281  contents.push_back(fname.get_basename());
282  fullpath = AAssetDir_getNextFileName(asset_dir);
283  }
284 
285  return true;
286 }
287 
288 ////////////////////////////////////////////////////////////////////
289 // Function: VirtualFileMountAndroidAsset::AssetStream::Destructor
290 // Access: Public, Virtual
291 // Description:
292 ////////////////////////////////////////////////////////////////////
293 VirtualFileMountAndroidAsset::AssetStream::
294 ~AssetStream() {
295  delete rdbuf();
296 }
297 
298 ////////////////////////////////////////////////////////////////////
299 // Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Constructor
300 // Access: Public
301 // Description:
302 ////////////////////////////////////////////////////////////////////
303 VirtualFileMountAndroidAsset::AssetStreamBuf::
304 AssetStreamBuf(AAsset *asset) :
305  _asset(asset) {
306 
307 #ifdef PHAVE_IOSTREAM
308  char *buf = new char[4096];
309  char *ebuf = buf + 4096;
310  setg(buf, ebuf, ebuf);
311 
312 #else
313  allocate();
314  setg(base(), ebuf(), ebuf());
315 #endif
316 }
317 
318 ////////////////////////////////////////////////////////////////////
319 // Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Destructor
320 // Access: Public, Virtual
321 // Description:
322 ////////////////////////////////////////////////////////////////////
323 VirtualFileMountAndroidAsset::AssetStreamBuf::
324 ~AssetStreamBuf() {
325  AAsset_close(_asset);
326 }
327 
328 ////////////////////////////////////////////////////////////////////
329 // Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekoff
330 // Access: Public, Virtual
331 // Description: Implements seeking within the stream.
332 ////////////////////////////////////////////////////////////////////
333 streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
334 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
335  size_t n = egptr() - gptr();
336 
337  int whence;
338  switch (dir) {
339  case ios_base::beg:
340  whence = SEEK_SET;
341  break;
342  case ios_base::cur:
343  if (off == 0) {
344  // Just requesting the current position,
345  // no need to void the buffer.
346  return AAsset_seek(_asset, 0, SEEK_CUR) - n;
347 
348  } else if (gptr() + off >= eback() && gptr() + off < egptr()) {
349  // We can seek around within the buffer.
350  gbump(off);
351  return AAsset_seek(_asset, 0, SEEK_CUR) - n + off;
352  }
353  whence = SEEK_CUR;
354  break;
355  case ios_base::end:
356  whence = SEEK_END;
357  break;
358  default:
359  return pos_type(-1);
360  }
361  gbump(n);
362  return AAsset_seek(_asset, off, whence);
363 }
364 
365 ////////////////////////////////////////////////////////////////////
366 // Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekpos
367 // Access: Public, Virtual
368 // Description: A variant on seekoff() to implement seeking within a
369 // stream.
370 //
371 // The MSDN Library claims that it is only necessary to
372 // redefine seekoff(), and not seekpos() as well, as the
373 // default implementation of seekpos() is supposed to
374 // map to seekoff() exactly as I am doing here; but in
375 // fact it must do something else, because seeking
376 // didn't work on Windows until I redefined this
377 // function as well.
378 ////////////////////////////////////////////////////////////////////
379 streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
380 seekpos(streampos pos, ios_openmode which) {
381  size_t n = egptr() - gptr();
382  gbump(n);
383  return AAsset_seek(_asset, pos, SEEK_SET);
384 }
385 
386 ////////////////////////////////////////////////////////////////////
387 // Function: VirtualFileMountAndroidAsset::AssetStreamBuf::underflow
388 // Access: Protected, Virtual
389 // Description: Called by the system istream implementation when its
390 // internal buffer needs more characters.
391 ////////////////////////////////////////////////////////////////////
392 int VirtualFileMountAndroidAsset::AssetStreamBuf::
393 underflow() {
394  // Sometimes underflow() is called even if the buffer is not empty.
395  if (gptr() >= egptr()) {
396  // Mark the buffer filled (with buffer_size bytes).
397  size_t buffer_size = egptr() - eback();
398  gbump(-(int)buffer_size);
399 
400  streamsize read_count;
401  read_count = AAsset_read(_asset, gptr(), buffer_size);
402 
403  if (read_count != buffer_size) {
404  // Oops, we didn't read what we thought we would.
405  if (read_count == 0) {
406  gbump(buffer_size);
407  return EOF;
408  }
409 
410  // Slide what we did read to the top of the buffer.
411  nassertr(read_count < buffer_size, EOF);
412  size_t delta = buffer_size - read_count;
413  memmove(gptr() + delta, gptr(), read_count);
414  gbump(delta);
415  }
416  }
417 
418  return (unsigned char)*gptr();
419 }
420 
421 #endif // ANDROID
virtual bool read_file(const Filename &file, bool do_uncompress, pvector< unsigned char > &result) const
Fills up the indicated pvector with the contents of the file, if it is a regular file.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:29
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85