Panda3D

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