From bc831f05b06477ca1a7f4dd7572b8245c100e190 Mon Sep 17 00:00:00 2001 From: kts Date: Sun, 1 Mar 2015 02:17:12 -0800 Subject: [PATCH] AssetManager/AssetCache system now mostly in place. AssetCache(s) create/read from a .CACHE file in the target directory. This file is human-readable and is the index for the cache. Each line contains the file's path, its checksum, and the filesize. This file is updated when the filesize of the file changes. Currently new files are found and added on start - to be added is a refresh function. Also to be added is an update via modification time - this will require a new entry in the cache index file. A single-line version heading should also be added. Data files are now loaded from the Cache - Mesh and Program build from passed buffers now. These are generally read from a loaded Asset. Next up: updating it to work on Linux, Android, iOS, and OS X. Also added a somewhat poor HashTable template implementation. Works okay, but might not be the best. --- build/vs/vs.vcxproj | 1 + build/vs/vs.vcxproj.filters | 3 + data/.CACHE | 10 ++ src/Asset.cpp | 21 +++- src/Asset.hpp | 14 ++- src/AssetCache.cpp | 179 ++++++++++++++++++++++++++- src/AssetCache.hpp | 8 +- src/AssetManager.cpp | 52 ++++++-- src/AssetManager.hpp | 6 +- src/Core.cpp | 35 ++++-- src/HashTable.hpp | 233 ++++++++++++++++++++++++++++++++++++ src/Mesh.cpp | 52 +++++--- src/Mesh.hpp | 4 +- src/Program.cpp | 13 +- src/Program.hpp | 2 +- src/fio.cpp | 12 +- 16 files changed, 581 insertions(+), 64 deletions(-) create mode 100644 data/.CACHE create mode 100644 src/HashTable.hpp diff --git a/build/vs/vs.vcxproj b/build/vs/vs.vcxproj index b9a8796..e5a7241 100644 --- a/build/vs/vs.vcxproj +++ b/build/vs/vs.vcxproj @@ -117,6 +117,7 @@ xcopy /s /e /d /y "..\..\data" "$(OutDir)data\" + diff --git a/build/vs/vs.vcxproj.filters b/build/vs/vs.vcxproj.filters index b12241d..c92e378 100644 --- a/build/vs/vs.vcxproj.filters +++ b/build/vs/vs.vcxproj.filters @@ -157,5 +157,8 @@ Classes + + Classes + \ No newline at end of file diff --git a/data/.CACHE b/data/.CACHE new file mode 100644 index 0000000..1d13bf0 --- /dev/null +++ b/data/.CACHE @@ -0,0 +1,10 @@ +data/shaders/fb_fs.100.glsl 0 259 +data/models/cube.obj 825768822 432 +data/shaders/fb_vs.100.glsl 0 177 +data/shaders/default_fs.100.glsl 0 244 +data/shaders/default_vs.100.glsl 0 334 +data/shaders/fb_fs.glsl 3713587946 233 +data/shaders/fb_vs.glsl 2722618720 177 +data/shaders/default_fs.glsl 1466891365 218 +data/shaders/default_vs.glsl 685822220 336 +data/models/chest.obj 870013261 4806 diff --git a/src/Asset.cpp b/src/Asset.cpp index f6ab4e8..2daa577 100644 --- a/src/Asset.cpp +++ b/src/Asset.cpp @@ -4,12 +4,31 @@ Asset #include "Asset.hpp" #include Asset::Asset() { - flags = 0; + flags = IS_NULL; data = NULL; data_length = 0; + data_checksum = 0; } Asset::~Asset() { if (flags & IS_LOADED) { free(data); } } +std::string Asset::getName() { + return filename; +} +char* Asset::getData() { + return data; +} +off_t Asset::getDataLength() { + return data_length; +} +uint32_t Asset::getChecksum() { + return data_checksum; +} +void Asset::unloadData() { + if (flags & IS_LOADED) { + free(data); + } + flags &= ~IS_LOADED; +} \ No newline at end of file diff --git a/src/Asset.hpp b/src/Asset.hpp index 2f6a174..6626da5 100644 --- a/src/Asset.hpp +++ b/src/Asset.hpp @@ -6,20 +6,28 @@ Our basic Asset class. This class exists as a container for data, but does not d #ifndef ASSET_HPP #define ASSET_HPP #include +#include "checksum.hpp" class Asset { friend class AssetManager; + friend class AssetCache; public: Asset(); ~Asset(); enum FLAGS { - IS_LOADED = (1 << 1) // indicates data is populated + IS_NULL = (1 << 1), // + IS_LOADED = (1 << 2) // indicates data is populated }; + std::string getName(); + char *getData(); + off_t getDataLength(); + uint32_t getChecksum(); + void unloadData(); protected: unsigned int flags; // Our asset state flags std::string filename; // Our filename, relative to the assets directory - std::string data_checksum; // Our full data checksum + uint32_t data_checksum; // Our full data checksum char *data; // Our data, NULL if IS_LOADED is not set - size_t data_length; // Our data's length, in bytes + off_t data_length; // Our data's length, in bytes }; #endif \ No newline at end of file diff --git a/src/AssetCache.cpp b/src/AssetCache.cpp index e1285ed..9f47324 100644 --- a/src/AssetCache.cpp +++ b/src/AssetCache.cpp @@ -1,12 +1,57 @@ #include "AssetCache.hpp" +#include "Log.hpp" +#include "checksum.hpp" +#include "fio.hpp" +#include +#if _WIN32 +#include +#include +#endif /* ======== Construction and Destruction ======== */ AssetCache::AssetCache() { + //assets.resize(128); + assets = new HashTable(128); + assets->setnull(NULL); +} +AssetCache::AssetCache(const char *cache_file) { + assets = new HashTable(128); + assets->setnull(NULL); + fromDir(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()); + if (asset != NULL) delete asset; + } } /* ======== Cache loading ======== */ int AssetCache::fromFile(const char *cache_file) { + int ret; + FILE *file = asset_fopen(cache_file, "rb"); + if (file == NULL) { + LOG(LOG_ERROR) << "Could not open cache file: " << cache_file; + return 1; + } + char fpath[1024]; + uint32_t fsum = 0; + size_t fsize = 0; +#ifdef _WIN32 + while ((ret = fscanf_s(file, "%s %u %u\n", fpath, 1024, &fsum, &fsize)) != EOF) { +#else + while ((ret = fscanf(file, "%s %u %u\n", fpath, &fsum, &fsize)) != EOF) { +#endif + Asset *asset = new Asset(); + asset->filename.assign(fpath); + asset->flags &= ~Asset::IS_NULL; + asset->data_checksum = fsum; + asset->data_length = fsize; + assets->set(fpath, asset); + LOG(LOG_DEBUG) << "opened " << fpath << " " << fsum; + } return 0; } /* fromDir @@ -14,14 +59,130 @@ This function will first attempt to load the .CACHE file contained in the given If not, the function will traverse the full hierarchy of the given directory and populate the cache accordingly. */ int AssetCache::fromDir(const char *dir) { + char cachepath[MAX_PATH]; +#ifndef _WIN32 + sprintf(cachepath, "%s/.CACHE", dir); +#else + sprintf_s(cachepath, MAX_PATH, "%s/.CACHE", dir); +#endif + if (fromFile(cachepath) != 0) { + LOG(LOG_INFO) << FUNC_NAME << ": " << cachepath << " does not exist"; + } + directory.assign(dir); + traverseDir_r(dir); + // TODO: move this to a "genChecksum()" sort of function + /*HashEntry *entry; + while ((entry = assets->iterate()) != NULL) { + Asset *asset = (Asset*)(entry->data); + // update the checksum if it doesn't exist yet. + if (asset->data_checksum == 0) { + char *data_buffer = NULL; + size_t data_len = asset_fileToMem(asset->filename.c_str(), &(data_buffer)); + asset->data_checksum = crc32(1337, data_buffer, data_len); + if (data_buffer != NULL) free(data_buffer); + } + } */ + return 0; +} +int AssetCache::traverseDir_r(const char *dir) { + size_t dir_len = strlen(dir)+1; +#if _WIN32 + WIN32_FIND_DATA ffd; + HANDLE h_find = INVALID_HANDLE_VALUE; + char wdir[MAX_PATH]; + + StringCbCopyN(wdir, MAX_PATH, dir, dir_len); + StringCbCatN(wdir, MAX_PATH, "/*", 2); + + if ((h_find = FindFirstFile(wdir, &ffd)) == INVALID_HANDLE_VALUE) { + LOG(LOG_ERROR) << FUNC_NAME << " " << wdir << " FindFirstFile error " << GetLastError(); + return 1; + } + do { + if (strcmp(ffd.cFileName, "..") == 0 || strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, ".CACHE") == 0) continue; + char fpath[MAX_PATH]; + StringCbCopyN(fpath, MAX_PATH, dir, dir_len); + StringCbCatN(fpath, MAX_PATH, "/", 2); + StringCbCatN(fpath, MAX_PATH, ffd.cFileName, strlen(ffd.cFileName)+1); + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + traverseDir_r(fpath); + } else { + Asset *asset = NULL; + if (assets->exists(fpath)) { + asset = assets->get(fpath); + if (ffd.nFileSizeLow != asset->data_length) { + LOG(LOG_DEBUG) << " file " << fpath << " changed from " << asset->data_length << " to " << ffd.nFileSizeLow; + asset->data_length = ffd.nFileSizeLow; + // TODO: recalculate checksum? + } + } else { + asset = new Asset(); + asset->filename.assign(fpath); + asset->flags &= ~Asset::IS_NULL; + // FIXME: this is not the right way to do this. We also should use nFileSizeHigh. Also, this does not match the file's actual bytes + asset->data_length = ffd.nFileSizeLow; + assets->set(fpath, asset); + } + } + } while (FindNextFile(h_find, &ffd)); + FindClose(h_find); +#else + struct dirent **files; + struct stat file_stat; + int n, i; + char udir[MAX_PATH]; + char filepath[MAX_PATH]; + n = scandir(dir, &files, NULL, alphasort); + if (n < 0) { + LOG(LOG_INFO) << FUNC_NAME << ": No files in dir " << dir; + return 2; + } + for (i = 0; i < n; i++) { + snprintf(filepath, "%s/%s", dir, files[i]->dname); + stat(filepath, &file_stat); + switch(file_stat.st_mode & S_IFMT) { + case S_IFDIR: + traverseDir_r(filepath); + break; + case S_IFREG: + Asset *asset = assets->get(fpath); + if (asset == NULL) { + asset = new Asset(); + asset->filename.assign(fpath); + } + asset->flags &= ~Asset::IS_NULL; + assets->set(fpath, asset); + break; + case S_IFLNK: + // TODO: travelDir_r if dir? + break; + default: + // TODO: dunno + break; + } + } +#endif return 0; } /* ======== Cache saving ======== */ -/* saveTo +/* toFile This function saves the AssetCache to the given asset cache file */ -int AssetCache::saveTo(const char *cache_file) { +int AssetCache::toFile(const char *cache_file) { + FILE *file = asset_fopen(cache_file, "wb"); + if (file == NULL) { + LOG(LOG_ERROR) << FUNC_NAME << ": Could not open cache file: " << cache_file << " " << errno; + 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); + LOG(LOG_DEBUG) << ": saving " << asset->filename.c_str() << " bytes " << asset->data_length; + } + fclose(file); return 0; } /* ======== Cache updating and validation ======== */ @@ -43,13 +204,23 @@ This function attempts to return the given asset if it exists. If it does not ex The file _is not_ loaded. */ Asset* AssetCache::getAsset(const char *filename) { - return NULL; + Asset *asset = assets->get(filename); + return asset; } /* loadAsset This function calls getAsset then loads the asset data into memory */ Asset* AssetCache::loadAsset(const char *filename) { - return NULL; + Asset *asset = getAsset(filename); + if (asset == NULL) return NULL; + if (!(asset->flags & Asset::IS_LOADED)) { + asset->data_length = asset_fileToMem(asset->filename.c_str(), &(asset->data)); + asset->flags |= Asset::IS_LOADED; + if (asset->data_checksum == 0) { + asset->data_checksum = crc32(1337, asset->data, asset->data_length); + } + } + return asset; } /* saveAsset This function saves the given asset's data to the corresponding data file diff --git a/src/AssetCache.hpp b/src/AssetCache.hpp index ab2b889..02a7be9 100644 --- a/src/AssetCache.hpp +++ b/src/AssetCache.hpp @@ -5,17 +5,20 @@ Asset Cache #ifndef ASSETCACHE_HPP #define ASSETCACHE_HPP #include "Asset.hpp" +#include "HashTable.hpp" #include class AssetCache { friend class AssetManager; public: AssetCache(); + AssetCache(const char *cache_dir); ~AssetCache(); // load 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 saveTo(const char *cache_file); // saves the loaded cache to the given file + int toFile(const char *cache_file); // saves the loaded cache to the given file // update int validateCache(); // checks if cache files are valid and exist int updateCache(); // scans directory for changes and updates the cache accordingly @@ -25,7 +28,8 @@ class AssetCache { 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: - std::vector asset; // TODO: replace with HashTable + //std::vector asset; // TODO: replace with HashTable + HashTable *assets; std::string directory; }; #ifdef __ANDROID__ diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp index 20437f2..73a0f86 100644 --- a/src/AssetManager.cpp +++ b/src/AssetManager.cpp @@ -19,21 +19,51 @@ Following this, the USER AssetCache is loaded, and are: #include "checksum.hpp" AssetManager::AssetManager() { + null_asset.filename = "NULL"; } AssetManager::~AssetManager() { + AssetCache *cache; + int i; + for (i = caches.size()-1; i >= 0; i--) { + try { + cache = caches.at(i); + } catch (const std::out_of_range& oor) { + LOG(LOG_ERROR) << FUNC_NAME << ": cache index out of range: " << oor.what(); + continue; + } + // FIXME: should we even save the caches from here? + // save the cache to its proper CACHE file + char cachepath[1024]; +#ifndef _WIN32 + sprintf(cachepath, "%s/.CACHE", cache->directory.c_str()); +#else + sprintf_s(cachepath, 1024, "%s/.CACHE", cache->directory.c_str()); +#endif + cache->toFile(cachepath); + // annd delete! + delete cache; + } } /* ======== Asset Loading ======== */ Asset* AssetManager::loadFile(const char *filename) { - LOG(LOG_INFO) << FUNC_NAME << " " << filename << ": " << std::hex << crc32(1337, filename, strlen(filename)); - // read in the file and get its checksum - char *buffer = NULL; - size_t len = asset_fileToMem(filename, &buffer); - if (len == 0) { - LOG(LOG_ERROR) << FUNC_NAME << " read length was 0, could not load file " << filename; - return NULL; + Asset *asset = NULL; + AssetCache *cache = NULL; + int i; + for (i = caches.size()-1; i >= 0; i--) { + try { + cache = caches.at(i); + } catch (const std::out_of_range& oor) { + LOG(LOG_ERROR) << FUNC_NAME << ": cache index out of range: " << oor.what(); + continue; + } + if ((asset = cache->loadAsset(filename)) != NULL) return asset; } - LOG(LOG_INFO) << FUNC_NAME << " " << filename << " " << std::hex << crc32(1337, buffer, len); - LOG(LOG_INFO) << filename << ": " << std::hex << crc32(1337, filename, strlen(filename)) << " " << std::hex << crc32(1337, buffer, len) << " " << std::hex << crc32(1337, buffer, len/2) << " " << std::hex << crc32(1337, buffer+(len/2), len/2); - free(buffer); - return NULL; + // if we got to here, we failed to find an asset + asset = &null_asset; + return asset; } +/* ======== Asset Cache ======== */ +int AssetManager::addCache(AssetCache *cache) { + caches.push_back(cache); + return 0; +} \ No newline at end of file diff --git a/src/AssetManager.hpp b/src/AssetManager.hpp index abdbc22..8330b0d 100644 --- a/src/AssetManager.hpp +++ b/src/AssetManager.hpp @@ -33,10 +33,12 @@ class AssetManager { // Asset* loadAsset(const char *uuid); // saves the loaded assets in the asset cache // saves all Assets in assets to the given cache file + int addCache(AssetCache* cache); // int saveCache(const char *cachefile); // // int loadCache(const char *cachefile) private: - std::vector caches; // Caches + Asset null_asset; // Special null asset to return from loadAsset if it could not be found + std::vector caches; // Caches AssetCache live_cache; // Cache of loaded assets -}; +}; \ No newline at end of file diff --git a/src/Core.cpp b/src/Core.cpp index 4912080..21b50eb 100644 --- a/src/Core.cpp +++ b/src/Core.cpp @@ -20,7 +20,7 @@ Core::Core() { asset_manager = NULL; } Core::~Core() { - + if (asset_manager != NULL) delete asset_manager; } int Core::initSystem() { if (SDL_Init(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) != 0) { @@ -80,6 +80,11 @@ int Core::initSystem() { // TEMP: this should be assigned elsewhere. 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("")); +#else + asset_manager->addCache(new AssetCache("data")); +#endif // TODO: load USER cache as second // TODO: add others via user pref? @@ -114,28 +119,36 @@ int Core::initSystem() { return 1; } #else - if (program->addShader("data/shaders/fb_fs.glsl", GL_FRAGMENT_SHADER) != 0) { + Asset *shader_asset = asset_manager->loadFile("data/shaders/fb_fs.glsl"); + if (program->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_FRAGMENT_SHADER) != 0) { LOG(LOG_ERROR) << "Failed to add Shader!"; return 1; } - if (program->addShader("data/shaders/fb_vs.glsl", GL_VERTEX_SHADER) != 0) { + shader_asset->unloadData(); + shader_asset = asset_manager->loadFile("data/shaders/fb_vs.glsl"); + if (program->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_VERTEX_SHADER) != 0) { LOG(LOG_ERROR) << "Failed to add Shader!"; return 1; } + shader_asset->unloadData(); if (program->doCompile() != 0) { LOG(LOG_ERROR) << "Failed to compile GLSL Program!"; return 1; } // TEMP: our model rendering program Program *program_model = new Program(); - if (program_model->addShader("data/shaders/default_fs.glsl", GL_FRAGMENT_SHADER) != 0) { + shader_asset = asset_manager->loadFile("data/shaders/default_fs.glsl"); + if (program_model->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_FRAGMENT_SHADER) != 0) { LOG(LOG_ERROR) << "Failed to add Shader!"; return 1; } - if (program_model->addShader("data/shaders/default_vs.glsl", GL_VERTEX_SHADER) != 0) { - LOG(LOG_ERROR) << "Failed to add Shdaer!"; + shader_asset->unloadData(); + shader_asset = asset_manager->loadFile("data/shaders/default_vs.glsl"); + if (program_model->addShader(shader_asset->getData(), shader_asset->getDataLength(), GL_VERTEX_SHADER) != 0) { + LOG(LOG_ERROR) << "Failed to add Shader!"; return 1; } + shader_asset->unloadData(); if (program_model->doCompile() != 0) { LOG(LOG_ERROR) << "Failed to compile GLSL Program!"; return 1; @@ -157,16 +170,20 @@ int Core::initSystem() { RenderSet *sert = new RenderSet(); sert->setProgram(program_model); - asset_manager->loadFile("data/models/cube.obj"); + Asset *asset_mesh; RenderObject *objerct = new RenderObject(); - Mesh *mersh = new Mesh("data/models/cube.obj"); + asset_mesh = asset_manager->loadFile("data/models/cube.obj"); + Mesh *mersh = new Mesh(asset_mesh->getData(), asset_mesh->getDataLength()); + asset_mesh->unloadData(); mersh->buildMesh(); objerct->setMesh(mersh); sert->addObject(objerct); objerct = new RenderObject(); - mersh = new Mesh("data/models/chest.obj"); + asset_mesh = asset_manager->loadFile("data/models/chest.obj"); + mersh = new Mesh(asset_mesh->getData(), asset_mesh->getDataLength()); + asset_mesh->unloadData(); mersh->buildMesh(); objerct->setMesh(mersh); objerct->setTranslation(-5.0f, 0.0f, 5.0f); diff --git a/src/HashTable.hpp b/src/HashTable.hpp new file mode 100644 index 0000000..ba5d724 --- /dev/null +++ b/src/HashTable.hpp @@ -0,0 +1,233 @@ +/* ================================================================ +HashTable +```````````````` +HashTable is a template class for storing the provided data type T along with an identifier key as a char*. Values are stored in the predefined HashTable with a hash of their key used as the position offset. Data collisions are handled by storing data in a HashEntry object and checking if the key itself does not match a stored copy. If the key does not match, the HashEntry provides a pointer to the next HashEntry. This continues on as a linked list until a match is or is not found. + +If a match is not found, the HashTable returns a special user-defined "null" variable that should match the provided data type to be stored. + +A HashTable may be resized, but it should _ONLY_ be resized if there is _NO_ data in the table. Future iterations of this template may allow for resizing by creating a new table, copying over the old stored data into their new positions, then replacing the old table with the new one. +================================================================ */ +#ifndef HASHTABLE_HPP +#define HASHTABLE_HPP +#include // malloc +#include // strlen +#include // INT_MAX +#include + +template class HashEntry { + public: + HashEntry(const char *key_, T data_) { + int key_len = strlen(key_)+1; + key = (char*)malloc(key_len*sizeof(char)); + memcpy(key, key_, key_len*sizeof(char)); + data = data_; + next = NULL; + }; + ~HashEntry() { + free(key); + }; + char *key; + T data; + HashEntry *next; +}; + +template class HashTable { + public: + HashTable() { HashTable(128); }; + HashTable(int hash_size) { + pos = 0; // iterator pos + size = hash_size; + iter = NULL; + last_iter = NULL; + table = (HashEntry**)malloc(sizeof(HashEntry*)*size); + for (int i = 0; i < size; i++) { + table[i] = NULL; + } + }; + ~HashTable() { + for (int i = 0; i < size; i++) { + if (table[i] != NULL) delete table[i]; + } + free(table); + }; + // resize should _only_ be called when the HashTable has _no_ data! + void resize(int hash_size) { + size = hash_size; + table = (HashEntry**)realloc(table, sizeof(HashEntry*)*size); + for (int i = 0; i < size; i++) { + table[i] = NULL; + } + }; + void setnull(T null_value) { + null = null_value; + } + // + T get(const char* key) { + int key_hash = getHash(key); + // find our hash if it exists + HashEntry *entry = table[key_hash]; + while (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + return entry->data; + } + entry = entry->next; + } + // doesn't exist, return the user's null value + return null; + }; + bool exists(const char *key) { + int key_hash = getHash(key); + // find our hash if it exists + HashEntry *entry = table[key_hash]; + while (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + return true; + } + entry = entry->next; + } + // doesn't exist, return the user's null value + return false; + } + T set(const char *key, T value) { + int key_hash = getHash(key); + // find our hash if it exists + HashEntry *entry = table[key_hash]; + if (entry == NULL) { + table[key_hash] = new HashEntry(key, value); + return table[key_hash]->data; + } + while (entry->next != NULL) { + if (strcmp(entry->key, key) == 0) { + entry->data = value; + return entry->data; + } + entry = entry->next; + } + entry->next = new HashEntry(key, value); + return entry->next->data; + }; + void del(const char *key) { + int key_hash = getHash(key); + HashEntry *entry = table[key_hash]; + HashEntry *last_entry = NULL; + if (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + table[key_hash] = entry->next; + delete entry; + return; + } + entry = entry->next; + } + while (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + if (last_entry != NULL) last_entry->next = entry->next; + delete entry; + return; + } + last_entry = entry; + entry = entry->next; + } + } + int getHash(const char *key) { + int key_hash = 0; + int key_len = strlen(key); + // generate our hash + for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { + key_hash += key[i]; + } + // limit hash to the HashTable's size and return + return (key_hash % size); + } + /*T iterate() { + int i = 1; + while(i) { + if (iter == NULL) { + if (pos > size) { + iter = NULL; + pos = 0; + return null; + } + iter = table[pos++]; + } else { + if (iter->data != null) { + std::cout << "found one at" << i-1 << std::endl; + HashEntry *ret = iter; + iter = iter->next; + return ret->data; + } + } + } + return iter->data; + }*/ + template T operator[](char* key) { + int key_hash = 0; + int key_len = strlen(key); + // get our key hash + for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { + key_hash += key[i]; + } + // limit it to our HashTable's size + key_hash = key_hash % size; + // find our hash if it exists + HashEntry *entry = table[key_hash]; + while (entry != NULL) { + if (strcmp(entry->key, key) == 0) { + return entry->data; + } + entry = entry->next; + } + // doesn't exist, return the user's null value + return null; + } + template T& operator[](char *key) const { + int key_hash = 0; + int key_len = strlen(key); + // get our key hash + for (int i = 0; key_hash < INT_MAX && i < key_len; i++) { + key_hash += key[i]; + } + // limit it to our HashTable's size + key_hash = key_hash % size; + // find our hash if it exists + HashEntry *last_entry, *entry = table[key_hash]; + if (entry == NULL) { + table[key_hash] = new HashEntry(key, null); + return entry->data; + } + while (entry->next != NULL) { + if (strcmp(entry->key, key) == 0) { + return entry->data; + } + last_entry = entry; + entry = entry->next; + } + entry->next = new HashEntry(key, null); + return entry->next; + } + HashEntry *iterate() { + int i = 1; + while(i) { + if (iter == NULL) { + if (pos >= size) { + iter = NULL; + pos = 0; + return NULL; + } + iter = table[pos++]; + } else { + HashEntry *ret = iter; + iter = iter->next; + return ret; + } + } + return NULL; + } + private: + int size; + T null; // user defined "null" value + HashEntry **table; + int pos; + HashEntry *iter; + HashEntry *last_iter; +}; +#endif \ No newline at end of file diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 1cfecd3..bd983cc 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -10,11 +10,11 @@ Mesh::Mesh() { flags = 0; mode = GL_STATIC_DRAW; } -Mesh::Mesh(const char *filename) { +Mesh::Mesh(const char *buffer, size_t buffer_size) { nbo = uvbo = vbo = vao = tex = 0; flags = 0; mode = GL_STATIC_DRAW; - loadObj(filename); + loadObj(buffer, buffer_size); } Mesh::Mesh(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count) { nbo = uvbo = vbo = vao = tex = 0; @@ -27,32 +27,38 @@ Mesh::~Mesh() { if (flags & MESH_BUILT) destroyMesh(); } -int Mesh::loadObj(const char *filename) { +int Mesh::loadObj(const char *buffer, size_t buffer_size) { std::vector temp_vertices; std::vector temp_uvs; std::vector temp_normals; std::vector vertex_indices; std::vector uv_indices; std::vector normal_indices; - int ret; - FILE *file = asset_fopen(filename, "r"); - if (file == NULL) { - LOG(LOG_ERROR) << "Could not open model: " << filename; + if (buffer == NULL || buffer_size == 0) { + LOG(LOG_WARNING) << FUNC_NAME << ": passed buffer is empty"; return 1; } - char word[32]; + char word[128]; + char *cpos = (char*)buffer; + size_t offset = 0; + size_t bpos = 0; #ifdef _WIN32 - while ((ret = fscanf_s(file, "%s ", word)) != EOF) { + while (sscanf_s(cpos, "%s %n", word, 128, &offset) == 1) { #else - while ((ret = fscanf(file, "%s ", word)) != EOF) { + while (sscanf(cpos, "%s %n", word, &offset) == 1) { #endif + bpos += offset; + cpos += offset; if (strcmp(word, "v") == 0) { // vertices // "x y z w" as floats, with "w" being optional Vec3 vec; // our vertex - char word[32]; + char word[128]; char ch; int pos = 0, w_i = 0; - while ((ch = fgetc(file)) != EOF) { + while (bpos < buffer_size) { + ch = *cpos; + bpos++; + cpos++; if (ch == ' ' || ch == '\n') { if (pos == 0) { // x word[w_i] = '\0'; @@ -80,7 +86,10 @@ int Mesh::loadObj(const char *filename) { char word[32]; char ch; int pos = 0, w_i = 0; - while ((ch = fgetc(file)) != EOF) { + while (bpos < buffer_size) { + ch = *cpos; + bpos++; + cpos++; if (ch == ' ' || ch == '\n') { if (pos == 0) { // x word[w_i] = '\0'; @@ -105,7 +114,10 @@ int Mesh::loadObj(const char *filename) { char word[32]; char ch; int pos = 0, w_i = 0; - while ((ch = fgetc(file)) != EOF) { + while (bpos < buffer_size) { + ch = *cpos; + bpos++; + cpos++; if (ch == ' ' || ch == '\n') { if (pos == 0) { // x word[w_i] = '\0'; @@ -138,7 +150,10 @@ int Mesh::loadObj(const char *filename) { char ch; int w_i = 0, mode = 0; // read in our faces into our index vectors - while ((ch = fgetc(file)) != EOF) { + while (bpos < buffer_size) { + ch = *cpos; + bpos++; + cpos++; if (ch == ' ' || ch == '\n') { word[w_i] = '\0'; if (mode == 0) { // was in vertex, now in texture-coords @@ -171,12 +186,14 @@ int Mesh::loadObj(const char *filename) { // "s 1" or "s off" } else { // ergo, newline noms char ch; - while ((ch = fgetc(file)) != EOF) { + while (bpos < buffer_size) { + ch = *cpos; + bpos++; + cpos++; if (ch == '\n') break; } } } - fclose(file); // we're done, let's convert our face-derived indices to our actual mesh int i; int indices = vertex_indices.size(); @@ -200,6 +217,7 @@ int Mesh::loadObj(const char *filename) { flags |= MESH_LOADED; return 0; } + int Mesh::loadArrays(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count) { if (v_count > 0) { vertices.assign(in_vertices, in_vertices + v_count); diff --git a/src/Mesh.hpp b/src/Mesh.hpp index 1cbea71..db8983c 100644 --- a/src/Mesh.hpp +++ b/src/Mesh.hpp @@ -15,11 +15,11 @@ A Mesh is as the name indicates, a 3D mesh. class Mesh { public: Mesh(); - Mesh(const char *filename); + Mesh(const char *buffer, size_t buffer_size); Mesh(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count); ~Mesh(); // - int loadObj(const char *filename); + int loadObj(const char *buffer, size_t buffer_size); int loadArrays(Vec3 *in_vertices, int v_count, Vec2 *in_uvs, int uv_count, Vec3 *in_normals, int n_count); int buildMesh(); int destroyMesh(); diff --git a/src/Program.cpp b/src/Program.cpp index 22413e6..b5ee85a 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -19,28 +19,23 @@ Program::~Program() { } /* ======== Compilation and Linking ======== */ // add given shader to Program. During doCompile(), it is compiled and attached to the program -int Program::addShader(const char *file, GLenum type) { - // Load in our shader file - char *buffer = NULL; - size_t length = asset_fileToMem(file, &buffer); - buffer[length] = '\0'; +int Program::addShader(const char *buffer, size_t length, GLenum type) { if (buffer == NULL) { - LOG(LOG_ERROR) << FUNC_NAME << ": Could not open \"" << file << "\" for reading"; + LOG(LOG_ERROR) << FUNC_NAME << ": passed buffer was empty!"; return 1; } // Create our shader GLuint shader = glCreateShader(type); if (shader == 0) { - LOG(LOG_ERROR) << FUNC_NAME << ": Could not create shader for " << file; + LOG(LOG_ERROR) << FUNC_NAME << ": Could not create shader!"; return 2; } // Load GLSL program into OpenGL const char *c_str = buffer; - glShaderSource(shader, 1, &c_str, NULL); + glShaderSource(shader, 1, &c_str, (const int*)&length); // Push the shader and its type onto the appropriate vectors shader_prog.push_back(shader); shader_type.push_back(type); - free(buffer); return 0; } int Program::doCompile() { diff --git a/src/Program.hpp b/src/Program.hpp index ebc7ddd..33cc411 100644 --- a/src/Program.hpp +++ b/src/Program.hpp @@ -38,7 +38,7 @@ class Program { public: Program(); ~Program(); - int addShader(const char *file, GLenum type); + int addShader(const char *buffer, size_t length, GLenum type); int attachTexture(GLuint index, GLuint texture); int setTexture(GLuint index, GLuint texture); int doCompile(); diff --git a/src/fio.cpp b/src/fio.cpp index e5c742e..a2f19c8 100644 --- a/src/fio.cpp +++ b/src/fio.cpp @@ -15,7 +15,7 @@ If any file is attempted to be read or written to, fio.hpp should be included fi */ size_t asset_fileToMem(const char *filename, char **buffer) { char chunk[1024]; - FILE *file = asset_fopen(filename, "r"); + FILE *file = asset_fopen(filename, "rb"); if (file == NULL) { LOG(LOG_ERROR) << FUNC_NAME << ": could not open " << filename; return 0; @@ -25,6 +25,10 @@ size_t asset_fileToMem(const char *filename, char **buffer) { fseek(file, 0, SEEK_SET); *buffer = (char*)malloc(size*sizeof(char)); + if (*buffer == NULL) { + LOG(LOG_ERROR) << FUNC_NAME << ": could not allocate " << size << " bytes for asset " << filename; + return NULL; + } int n = 0; size_t offset = 0; while ((n = fread(chunk, 1, 1024, file))) { @@ -44,9 +48,11 @@ FILE *asset_fopen(const char *filename, const char *mode) { #ifdef __ANDROID__ return apk_fopen(filename, mode); #elif _WIN32 - FILE *file; + /*FILE *file; fopen_s(&file, filename, mode); - return file; + return file;*/ + // _fsopen is used as we pass a FILE ptr back - fopen_s does not allow this + return _fsopen(filename, mode, _SH_DENYNO); #else return fopen(filename, mode); #endif