diff --git a/src/Asset.hpp b/src/Asset.hpp index 6626da5..78a924d 100644 --- a/src/Asset.hpp +++ b/src/Asset.hpp @@ -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 \ No newline at end of file +#endif diff --git a/src/AssetCache.cpp b/src/AssetCache.cpp index 3297838..974823f 100644 --- a/src/AssetCache.cpp +++ b/src/AssetCache.cpp @@ -25,7 +25,6 @@ AssetCache::AssetCache(const char *cache_file) { } AssetCache::~AssetCache() { HashEntry *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, "/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., "/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 *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(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 diff --git a/src/AssetCache.hpp b/src/AssetCache.hpp index f070f54..4f4c2fd 100644 --- a/src/AssetCache.hpp +++ b/src/AssetCache.hpp @@ -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 diff --git a/src/Core.cpp b/src/Core.cpp index f357622..db6e493 100644 --- a/src/Core.cpp +++ b/src/Core.cpp @@ -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")); diff --git a/src/Log.cpp b/src/Log.cpp index 9fa464c..9eb8760 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -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"; diff --git a/src/Mesh.cpp b/src/Mesh.cpp index bd983cc..758f99e 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -1,6 +1,5 @@ #include "Mesh.hpp" #include "Log.hpp" -#include "fio.hpp" #include // fscanf #include // strcmp #include // 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) { diff --git a/src/Program.cpp b/src/Program.cpp index b5ee85a..3fe08a4 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -6,7 +6,6 @@ loading, creating, and compiling OpenGL programs. ================================================================ */ #include "Program.hpp" #include "Log.hpp" -#include "fio.hpp" #include #include // ostringstream #include // oor diff --git a/src/fio.cpp b/src/fio.cpp index d3dfd8b..0eede10 100644 --- a/src/fio.cpp +++ b/src/fio.cpp @@ -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 diff --git a/src/fio.hpp b/src/fio.hpp index b68d3b3..10ccc59 100644 --- a/src/fio.hpp +++ b/src/fio.hpp @@ -20,12 +20,15 @@ FILE *asset_fopen(const char *filename, const char *mode); #include 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