ApkAssetCache now fully works. Unlike AssetCache, it does not build a cache by traversing the directory hierarchy - this is due to limits of how the Android assets archive works -, but rather adds to the cache on get/load requests. This is more inefficient, but it shouldn't be much of a consideration. Fixed preprocessor conditionals around loops to stop vi from exploding. Some minor code cleanup to remove warnings.

master
kts 2015-03-01 16:46:13 -08:00
parent 977af72999
commit 6e0a0b3226
9 changed files with 113 additions and 22 deletions

View File

@ -11,6 +11,9 @@ Our basic Asset class. This class exists as a container for data, but does not d
class Asset {
friend class AssetManager;
friend class AssetCache;
#ifdef __ANDROID__
friend class ApkAssetCache;
#endif
public:
Asset();
~Asset();
@ -30,4 +33,4 @@ class Asset {
char *data; // Our data, NULL if IS_LOADED is not set
off_t data_length; // Our data's length, in bytes
};
#endif
#endif

View File

@ -25,7 +25,6 @@ AssetCache::AssetCache(const char *cache_file) {
}
AssetCache::~AssetCache() {
HashEntry<Asset*> *entry;
int i = 0;
while ((entry = assets->iterate()) != NULL) {
Asset *asset = (Asset*)(entry->data);
assets->del(asset->filename.c_str());
@ -43,23 +42,25 @@ int AssetCache::fromFile(const char *cache_file) {
}
char fpath[1024];
uint32_t fsum = 0;
size_t fsize = 0;
off_t fsize = 0;
#ifdef _WIN32
while ((ret = fscanf_s(file, "%s %u %u\n", fpath, 1024, &fsum, &fsize)) != EOF) {
while ((ret = fscanf_s(file, "%s %u %jd\n", fpath, 1024, &fsum, (intmax_t*)&fsize)) != EOF)
#else
while ((ret = fscanf(file, "%s %u %u\n", fpath, &fsum, &fsize)) != EOF) {
while ((ret = fscanf(file, "%s %u %jd\n", fpath, &fsum, (intmax_t*)&fsize)) != EOF)
#endif
{
char fullpath[1024];
snprintf(fullpath, 1024, "%s/%s", directory.c_str(), fpath);
// only add the asset if it exists.
#ifdef _WIN32
DWORD file_attrib = GetFileAttributes(fullpath);
if (file_attrib != INVALID_FILE_ATTRIBUTES) {
if (file_attrib != INVALID_FILE_ATTRIBUTES)
#else
// NOTE: TOCTOU/race vulnerability exists here, but I don't think it matters.
struct stat fstat;
if (stat(fullpath, &fstat) == 0) {
if (stat(fullpath, &fstat) == 0)
#endif
{
Asset *asset = new Asset();
asset->filename.assign(fpath);
asset->flags &= ~Asset::IS_NULL;
@ -105,7 +106,6 @@ int AssetCache::fromDir(const char *dir) {
int AssetCache::traverseDir_r(const char *dir) {
char full_dir[1024]; // "full" dir path, e.g, "<appdata>/data/models"
snprintf(full_dir, 1024, "%s/%s", directory.c_str(), dir);
size_t dir_len = strlen(dir)+1;
#if _WIN32
WIN32_FIND_DATA ffd;
HANDLE h_find = INVALID_HANDLE_VALUE;
@ -150,7 +150,6 @@ int AssetCache::traverseDir_r(const char *dir) {
struct dirent **files;
struct stat file_stat;
int n, i;
char udir[1024];
char fpath[1024]; // "full" file path, e.g., "<appdir>/data/models/cube.obj"
char lpath[1024]; // "local" file path, e.g., "data/models/cube.obj"
Asset *asset;
@ -218,10 +217,9 @@ int AssetCache::toFile(const char *cache_file) {
return 1;
}
HashEntry<Asset*> *entry;
int i = 0;
while ((entry = assets->iterate()) != NULL) {
Asset *asset = (Asset*)(entry->data);
fprintf(file, "%s %u %u\n", asset->filename.c_str(), asset->data_checksum, asset->data_length);
fprintf(file, "%s %u %jd\n", asset->filename.c_str(), asset->data_checksum, (intmax_t)asset->data_length);
LOG(LOG_DEBUG) << ": saving " << asset->filename.c_str() << " bytes " << asset->data_length;
}
fclose(file);
@ -278,3 +276,69 @@ This function attempts to create a new Asset using some provided data
Asset* AssetCache::createAsset(const char *filename, const char *data, size_t data_len) {
return NULL;
}
/* ================================================================
ApkAssetCache
----------------
Android-only Extended class of AssetCache that accessed the APK's asset archive. Access is read-only.
Unlike the standard AssetCache, ApkAssetCache does not build a cache by traversing the asset directory hierarchy, but rather adds to the cache during get/load requests. This is due to, AFAIK, the lack of directory traversal in the AAsset_* functions. getNextFileName only steps through files, not directories - this is probably due to the files not existing in a directory structure, but rather in a special assets archive that has its own rules.
This is somewhat more inefficient, but it should not be much of a bottleneck.
================================================================ */
#ifdef __ANDROID__
ApkAssetCache::ApkAssetCache(const char *cache_dir) {
assets = new HashTable<Asset*>(128);
assets->setnull(NULL);
fromDir(cache_dir);
}
int ApkAssetCache::fromFile(const char *cache_file) { return 0; }
int ApkAssetCache::fromDir(const char *cache_dir) {
directory.assign(cache_dir);
return 0;
}
int ApkAssetCache::traverseDir_r(const char *dir) { return 0; }
// NOTE: since we cannot traverse the apk assets AFAIK, we just try to add it to the cache on getAsset. This somewhat inefficient, but it will have to do.
Asset* ApkAssetCache::getAsset(const char *filename) {
Asset *asset = assets->get(filename);
// if asset doesn't exist, let's check the apk
if (asset == NULL) {
std::string fullpath = directory;
if (directory.size() > 0) fullpath.append("/");
fullpath.append(filename);
AAsset* aasset = AAssetManager_open(android_asset_manager, fullpath.c_str(), 0);
if (aasset != NULL) {
asset = new Asset();
asset->filename.assign(filename);
asset->data_length = AAsset_getLength(aasset);
asset->flags &= ~Asset::IS_NULL;
assets->set(filename, asset);
}
AAsset_close(aasset);
}
return asset;
}
Asset* ApkAssetCache::loadAsset(const char *filename) {
Asset *asset = getAsset(filename);
if (asset == NULL) return NULL;
if (!(asset->flags & Asset::IS_LOADED)) {
std::string fullpath = directory;
if (directory.size() > 0) fullpath.append("/");
fullpath.append(filename);
AAsset *aasset = AAssetManager_open(android_asset_manager, fullpath.c_str(), 0);
const void *aasset_buffer = AAsset_getBuffer(aasset);
if (aasset_buffer != NULL) {
off_t data_size = AAsset_getLength(aasset) * sizeof(char);
asset->data = (char*)malloc(data_size);
memcpy((asset->data), aasset_buffer, data_size);
asset->flags |= Asset::IS_LOADED;
if (asset->data_checksum == 0) {
asset->data_checksum = crc32(1337, asset->data, asset->data_length);
}
} else {
LOG(LOG_WARNING) << FUNC_NAME << ": Could not open AAsset buffer!";
}
AAsset_close(aasset);
}
return asset;
}
#endif

View File

@ -23,8 +23,8 @@ class AssetCache {
int validateCache(); // checks if cache files are valid and exist
int updateCache(); // scans directory for changes and updates the cache accordingly
// get/add
Asset *getAsset(const char *filename); // attempts to get the given asset via filename
Asset *loadAsset(const char *filename); // attempts to get the given asset and load its contents into memory
virtual Asset *getAsset(const char *filename); // attempts to get the given asset via filename
virtual Asset *loadAsset(const char *filename); // attempts to get the given asset and load its contents into memory
Asset *saveAsset(const char *filename); // saves the given asset to disk
Asset *createAsset(const char *filename, const char *data, size_t data_len); // create a new Asset, populating it with given data
protected:
@ -35,7 +35,20 @@ class AssetCache {
#ifdef __ANDROID__
class ApkAssetCache : public AssetCache {
public:
ApkAssetCache(const char *cache_dir) : AssetCache(cache_dir) {};
ApkAssetCache(const char *cache_dir);
int fromFile(const char *cache_file); // load a cache file
int fromDir(const char *dir); // create/load a cache from the given directory
int traverseDir_r(const char *dir); // recursive function for adding Asset(s) from a given directory to the cache
// save
int toFile(const char *cache_file) {};
// update
int validateCache() {}; // checks if cache files are valid and exist
int updateCache() {}; // scans directory for changes and updates the cache accordingly
// get/add
virtual Asset *getAsset(const char *filename); // attempts to get the given asset via filename
virtual Asset *loadAsset(const char *filename); // attempts to get the given asset and load its contents into memory
Asset *saveAsset(const char *filename) {}; // saves the given asset to disk
Asset *createAsset(const char *filename, const char *data, size_t data_len) {}; // create a new Asset, populating it with given data
};
#endif
#endif

View File

@ -80,8 +80,11 @@ int Core::initSystem() {
asset_manager = new AssetManager();
// TODO: load APP cache as first cache (using ApkAssetCache in the case of Android)
#ifdef __ANDROID__
//asset_manager->addCache(new ApkAssetCache(""));
// Load the built-in RO ApkAssetCache directory first
asset_manager->addCache(new ApkAssetCache("data"));
// Load in the application's RW location on the install location
asset_manager->addCache(new AssetCache(android_app_dir.c_str()));
// Load in the application's RW external location
asset_manager->addCache(new AssetCache(android_ext_dir.c_str()));
#else
asset_manager->addCache(new AssetCache("data"));

View File

@ -30,7 +30,7 @@ std::ostringstream& Log::Get(LogLevel level) {
}
Log::~Log() {
os << std::endl;
char *title = "Undefined";
char const *title = "Undefined";
switch(l) {
case LOG_INFO:
title = "Info";

View File

@ -1,6 +1,5 @@
#include "Mesh.hpp"
#include "Log.hpp"
#include "fio.hpp"
#include <stdio.h> // fscanf
#include <string.h> // strcmp
#include <stdlib.h> // atof
@ -40,7 +39,7 @@ int Mesh::loadObj(const char *buffer, size_t buffer_size) {
}
char word[128];
char *cpos = (char*)buffer;
size_t offset = 0;
int offset = 0;
size_t bpos = 0;
#ifdef _WIN32
while (sscanf_s(cpos, "%s %n", word, 128, &offset) == 1) {

View File

@ -6,7 +6,6 @@ loading, creating, and compiling OpenGL programs.
================================================================ */
#include "Program.hpp"
#include "Log.hpp"
#include "fio.hpp"
#include <fstream>
#include <sstream> // ostringstream
#include <stdexcept> // oor

View File

@ -107,9 +107,9 @@ static int apk_close(void *asset) {
AAsset_close((AAsset*)asset);
return 0;
}
AAssetManager* asset_manager;
AAssetManager* android_asset_manager;
void apk_set_asset_manager(AAssetManager* manager) {
asset_manager = manager;
android_asset_manager = manager;
}
JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setAssetManager(JNIEnv* env, jobject obj, jobject assetManager) {
AAssetManager *mgr = AAssetManager_fromJava(env, assetManager);
@ -117,11 +117,15 @@ JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setAssetManager(JNIEnv* env,
}
FILE* apk_fopen(const char *filename, const char *mode) {
if (mode[0] == 'w') return NULL;
AAsset *asset = AAssetManager_open(asset_manager, filename, 0);
AAsset *asset = AAssetManager_open(android_asset_manager, filename, 0);
if (!asset) return NULL;
return funopen(asset, apk_read, apk_write, apk_seek, apk_close);
}
AAssetDir* apk_openDir(const char *dir) {
return AAssetManager_openDir(android_asset_manager, dir);
}
std::string android_app_dir;
std::string android_ext_dir;
JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setAppDirectory(JNIEnv* env, jobject obj, jstring app_dir) {
@ -137,4 +141,7 @@ JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setExtDirectory(JNIEnv* env,
android_ext_dir.assign(native_string);
env->ReleaseStringUTFChars(ext_dir, native_string);
}
AAssetManager *getAAssetManager() {
return android_asset_manager;
}
#endif

View File

@ -20,12 +20,15 @@ FILE *asset_fopen(const char *filename, const char *mode);
#include <android/asset_manager_jni.h>
extern std::string android_app_dir;
extern std::string android_ext_dir;
extern "C" AAssetManager* android_asset_manager;
extern "C" {
void apk_set_asset_manager(AAssetManager* manager);
FILE *apk_fopen(const char *filename, const char *mode);
AAssetDir* apk_openDir(const char *dir);
JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setAssetManager(JNIEnv* env, jobject obj, jobject assetManager);
JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setAppDirectory(JNIEnv* env, jobject obj, jstring app_dir);
JNIEXPORT void JNICALL Java_com_polymathic_RtB_RtB_setExtDirectory(JNIEnv* env, jobject obj, jstring ext_dir);
AAssetManager *getAAssetManager();
}
#endif
#endif