mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
421 lines
15 KiB
C++
421 lines
15 KiB
C++
// Filename: virtualFileMountAndroidAsset.cxx
|
|
// Created by: rdb (21Jan13)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef ANDROID
|
|
|
|
#include "virtualFileMountAndroidAsset.h"
|
|
#include "virtualFileSystem.h"
|
|
|
|
#ifndef NDEBUG
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
TypeHandle VirtualFileMountAndroidAsset::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
VirtualFileMountAndroidAsset::
|
|
~VirtualFileMountAndroidAsset() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::get_fd
|
|
// Access: Public
|
|
// Description: Returns a file descriptor that can be used to read
|
|
// the asset if it was stored uncompressed and
|
|
// unencrypted. Returns a valid fd or -1.
|
|
////////////////////////////////////////////////////////////////////
|
|
int VirtualFileMountAndroidAsset::
|
|
get_fd(const Filename &file, off_t *start, off_t *length) const {
|
|
AAsset* asset;
|
|
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
|
|
|
|
int fd = AAsset_openFileDescriptor(asset, start, length);
|
|
AAsset_close(asset);
|
|
return fd;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::has_file
|
|
// Access: Public, Virtual
|
|
// Description: Returns true if the indicated file exists within the
|
|
// mount system.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
has_file(const Filename &file) const {
|
|
return (file.empty() || is_directory(file) || is_regular_file(file));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::is_directory
|
|
// Access: Public, Virtual
|
|
// Description: Returns true if the indicated file exists within the
|
|
// mount system and is a directory.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
is_directory(const Filename &file) const {
|
|
// This is the only way - AAssetManager_openDir also works for files.
|
|
//AAssetDir *dir = AAssetManager_openDir(_asset_mgr, file.c_str());
|
|
|
|
//express_cat.error() << "is_directory " << file << " - " << dir << "\n";
|
|
|
|
//if (dir == NULL) {
|
|
// return false;
|
|
//}
|
|
//AAssetDir_close(dir);
|
|
|
|
// openDir doesn't return NULL for ordinary files!
|
|
return !is_regular_file(file);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::is_regular_file
|
|
// Access: Public, Virtual
|
|
// Description: Returns true if the indicated file exists within the
|
|
// mount system and is a regular file.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
is_regular_file(const Filename &file) const {
|
|
// I'm afraid the only way to see if it exists is to try and open it.
|
|
AAsset* asset;
|
|
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
|
|
|
|
express_cat.error() << "is_regular_file " << file << " - " << asset << "\n";
|
|
|
|
if (asset == NULL) {
|
|
return false;
|
|
}
|
|
AAsset_close(asset);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::read_file
|
|
// Access: Public, Virtual
|
|
// Description: Fills up the indicated pvector with the contents of
|
|
// the file, if it is a regular file. Returns true on
|
|
// success, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
read_file(const Filename &file, bool do_uncompress,
|
|
pvector<unsigned char> &result) const {
|
|
if (do_uncompress) {
|
|
// If the file is to be decompressed, we'd better just use the
|
|
// higher-level implementation, which includes support for
|
|
// on-the-fly decompression.
|
|
return VirtualFileMount::read_file(file, do_uncompress, result);
|
|
}
|
|
|
|
// But if we're just reading a straight file, let's just read
|
|
// it here to avoid all of the streambuf nonsense.
|
|
result.clear();
|
|
|
|
AAsset* asset;
|
|
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_STREAMING);
|
|
if (asset == (AAsset *)NULL) {
|
|
express_cat.info()
|
|
<< "Unable to read " << file << "\n";
|
|
}
|
|
|
|
// Reserve enough space to hold the entire file.
|
|
off_t file_size = AAsset_getLength(asset);
|
|
if (file_size == 0) {
|
|
return true;
|
|
} else if (file_size > 0) {
|
|
result.reserve((size_t)file_size);
|
|
}
|
|
|
|
static const size_t buffer_size = 4096;
|
|
char buffer[buffer_size];
|
|
|
|
int count = AAsset_read(asset, buffer, buffer_size);
|
|
while (count > 0) {
|
|
thread_consider_yield();
|
|
result.insert(result.end(), buffer, buffer + count);
|
|
count = AAsset_read(asset, buffer, buffer_size);
|
|
}
|
|
|
|
AAsset_close(asset);
|
|
return (count == 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::open_read_file
|
|
// Access: Public, Virtual
|
|
// Description: Opens the file for reading, if it exists. Returns a
|
|
// newly allocated istream on success (which you should
|
|
// eventually delete when you are done reading).
|
|
// Returns NULL on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
istream *VirtualFileMountAndroidAsset::
|
|
open_read_file(const Filename &file) const {
|
|
AAsset* asset;
|
|
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
|
|
if (asset == (AAsset *)NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
AssetStream *stream = new AssetStream(asset);
|
|
return (istream *) stream;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::get_file_size
|
|
// Access: Published, Virtual
|
|
// Description: Returns the current size on disk (or wherever it is)
|
|
// of the already-open file. Pass in the stream that
|
|
// was returned by open_read_file(); some
|
|
// implementations may require this stream to determine
|
|
// the size.
|
|
////////////////////////////////////////////////////////////////////
|
|
streamsize VirtualFileMountAndroidAsset::
|
|
get_file_size(const Filename &file, istream *in) const {
|
|
// If it's already open, get the AAsset pointer from the streambuf.
|
|
const AssetStreamBuf *buf = (const AssetStreamBuf *) in->rdbuf();
|
|
off_t length = AAsset_getLength(buf->_asset);
|
|
return length;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::get_file_size
|
|
// Access: Published, Virtual
|
|
// Description: Returns the current size on disk (or wherever it is)
|
|
// of the file before it has been opened.
|
|
////////////////////////////////////////////////////////////////////
|
|
streamsize VirtualFileMountAndroidAsset::
|
|
get_file_size(const Filename &file) const {
|
|
AAsset* asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
|
|
off_t length = AAsset_getLength(asset);
|
|
AAsset_close(asset);
|
|
return length;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::get_timestamp
|
|
// Access: Published, Virtual
|
|
// Description: Returns a time_t value that represents the time the
|
|
// file was last modified, to within whatever precision
|
|
// the operating system records this information (on a
|
|
// Windows95 system, for instance, this may only be
|
|
// accurate to within 2 seconds).
|
|
//
|
|
// If the timestamp cannot be determined, either because
|
|
// it is not supported by the operating system or
|
|
// because there is some error (such as file not found),
|
|
// returns 0.
|
|
////////////////////////////////////////////////////////////////////
|
|
time_t VirtualFileMountAndroidAsset::
|
|
get_timestamp(const Filename &file) const {
|
|
// There's no obvious way to get a timestamp from an Android asset.
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::get_system_info
|
|
// Access: Public, Virtual
|
|
// Description: Populates the SubfileInfo structure with the data
|
|
// representing where the file actually resides on disk,
|
|
// if this is knowable. Returns true if the file might
|
|
// reside on disk, and the info is populated, or false
|
|
// if it might not (or it is not known where the file
|
|
// resides), in which case the info is meaningless.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
get_system_info(const Filename &file, SubfileInfo &info) {
|
|
off_t start, length;
|
|
int fd = get_fd(file, &start, &length);
|
|
|
|
#ifndef NDEBUG
|
|
// Double-check that this fd actually points to the apk.
|
|
struct stat st1, st2;
|
|
nassertr(fstat(fd, &st1) == 0, false);
|
|
nassertr(stat(_apk_path.c_str(), &st2) == 0, false);
|
|
nassertr(st1.st_dev == st2.st_dev, false);
|
|
nassertr(st1.st_ino == st2.st_ino, false);
|
|
#endif
|
|
|
|
// We don't actually need the file descriptor, so close it.
|
|
close(fd);
|
|
|
|
info = SubfileInfo(_apk_path, start, length);
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::scan_directory
|
|
// Access: Public, Virtual
|
|
// Description: Fills the given vector up with the list of filenames
|
|
// that are local to this directory, if the filename is
|
|
// a directory. Returns true if successful, or false if
|
|
// the file is not a directory or cannot be read.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool VirtualFileMountAndroidAsset::
|
|
scan_directory(vector_string &contents, const Filename &dir) const {
|
|
AAssetDir *asset_dir = AAssetManager_openDir(_asset_mgr, dir.c_str());
|
|
if (asset_dir == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// Note: this returns the full path.
|
|
const char *fullpath = AAssetDir_getNextFileName(asset_dir);
|
|
|
|
while (fullpath != NULL) {
|
|
express_cat.error() << fullpath << "\n"; // DEBUG
|
|
// Determine the basename and add it to the vector.
|
|
Filename fname (fullpath);
|
|
contents.push_back(fname.get_basename());
|
|
fullpath = AAssetDir_getNextFileName(asset_dir);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStream::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
VirtualFileMountAndroidAsset::AssetStream::
|
|
~AssetStream() {
|
|
delete rdbuf();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
VirtualFileMountAndroidAsset::AssetStreamBuf::
|
|
AssetStreamBuf(AAsset *asset) :
|
|
_asset(asset) {
|
|
|
|
#ifdef PHAVE_IOSTREAM
|
|
char *buf = new char[4096];
|
|
char *ebuf = buf + 4096;
|
|
setg(buf, ebuf, ebuf);
|
|
|
|
#else
|
|
allocate();
|
|
setg(base(), ebuf(), ebuf());
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
VirtualFileMountAndroidAsset::AssetStreamBuf::
|
|
~AssetStreamBuf() {
|
|
AAsset_close(_asset);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekoff
|
|
// Access: Public, Virtual
|
|
// Description: Implements seeking within the stream.
|
|
////////////////////////////////////////////////////////////////////
|
|
streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
|
|
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
|
|
size_t n = egptr() - gptr();
|
|
|
|
int whence;
|
|
switch (dir) {
|
|
case ios_base::beg:
|
|
whence = SEEK_SET;
|
|
break;
|
|
case ios_base::cur:
|
|
if (off == 0) {
|
|
// Just requesting the current position,
|
|
// no need to void the buffer.
|
|
return AAsset_seek(_asset, 0, SEEK_CUR) - n;
|
|
|
|
} else if (gptr() + off >= eback() && gptr() + off < egptr()) {
|
|
// We can seek around within the buffer.
|
|
gbump(off);
|
|
return AAsset_seek(_asset, 0, SEEK_CUR) - n + off;
|
|
}
|
|
whence = SEEK_CUR;
|
|
break;
|
|
case ios_base::end:
|
|
whence = SEEK_END;
|
|
break;
|
|
default:
|
|
return pos_type(-1);
|
|
}
|
|
gbump(n);
|
|
return AAsset_seek(_asset, off, whence);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekpos
|
|
// Access: Public, Virtual
|
|
// Description: A variant on seekoff() to implement seeking within a
|
|
// stream.
|
|
//
|
|
// The MSDN Library claims that it is only necessary to
|
|
// redefine seekoff(), and not seekpos() as well, as the
|
|
// default implementation of seekpos() is supposed to
|
|
// map to seekoff() exactly as I am doing here; but in
|
|
// fact it must do something else, because seeking
|
|
// didn't work on Windows until I redefined this
|
|
// function as well.
|
|
////////////////////////////////////////////////////////////////////
|
|
streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
|
|
seekpos(streampos pos, ios_openmode which) {
|
|
size_t n = egptr() - gptr();
|
|
gbump(n);
|
|
return AAsset_seek(_asset, pos, SEEK_SET);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::underflow
|
|
// Access: Protected, Virtual
|
|
// Description: Called by the system istream implementation when its
|
|
// internal buffer needs more characters.
|
|
////////////////////////////////////////////////////////////////////
|
|
int VirtualFileMountAndroidAsset::AssetStreamBuf::
|
|
underflow() {
|
|
// Sometimes underflow() is called even if the buffer is not empty.
|
|
if (gptr() >= egptr()) {
|
|
// Mark the buffer filled (with buffer_size bytes).
|
|
size_t buffer_size = egptr() - eback();
|
|
gbump(-(int)buffer_size);
|
|
|
|
streamsize read_count;
|
|
read_count = AAsset_read(_asset, gptr(), buffer_size);
|
|
|
|
if (read_count != buffer_size) {
|
|
// Oops, we didn't read what we thought we would.
|
|
if (read_count == 0) {
|
|
gbump(buffer_size);
|
|
return EOF;
|
|
}
|
|
|
|
// Slide what we did read to the top of the buffer.
|
|
nassertr(read_count < buffer_size, EOF);
|
|
size_t delta = buffer_size - read_count;
|
|
memmove(gptr() + delta, gptr(), read_count);
|
|
gbump(delta);
|
|
}
|
|
}
|
|
|
|
return (unsigned char)*gptr();
|
|
}
|
|
|
|
#endif // ANDROID
|