|
@@ -0,0 +1,420 @@
|
|
|
|
|
+// 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.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+off_t 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.
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
|
|
+off_t 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
|