From fa3e9e732928a7914f6d797259bc041151709cb8 Mon Sep 17 00:00:00 2001 From: fgenesis Date: Thu, 15 Sep 2011 18:33:13 +0200 Subject: [PATCH] added partial VFS support - enough to read static data from any source --- Aquaria/Continuity.cpp | 13 +- Aquaria/DSQ.cpp | 129 ++++---- Aquaria/DSQ.h | 2 - Aquaria/Emote.cpp | 2 +- Aquaria/Game.cpp | 15 +- Aquaria/GameplayVariables.cpp | 3 +- Aquaria/Main.cpp | 6 +- Aquaria/ScriptInterface.cpp | 31 +- Aquaria/Shot.cpp | 6 +- Aquaria/StatsAndAchievements.cpp | 65 ++-- Aquaria/StringBank.cpp | 2 +- Aquaria/SubtitlePlayer.cpp | 2 +- Aquaria/WorldMapTiles.cpp | 3 +- BBGE/Base.cpp | 326 +++---------------- BBGE/Base.h | 17 +- BBGE/BitmapFont.cpp | 2 +- BBGE/Core.cpp | 97 ++++++ BBGE/Core.h | 10 + BBGE/FmodOpenALBridge.cpp | 128 ++++---- BBGE/Precacher.cpp | 2 +- BBGE/Shader.cpp | 44 +-- BBGE/SimpleIStringStream.h | 4 +- BBGE/SoundManager.cpp | 29 +- BBGE/TTFFont.cpp | 14 +- BBGE/Texture.cpp | 22 +- BBGE/VFSFileStream.cpp | 57 ++++ BBGE/VFSFileStream.h | 43 +++ BBGE/VFSIncludes.h | 9 + BBGE/glfont2.cpp | 72 ++--- BBGE/tinyxml.cpp | 36 ++- BBGE/tinyxml.h | 7 +- CMakeLists.txt | 6 + ExternalLibs/glpng.h | 2 + ExternalLibs/glpng/glpng.c | 337 +++++++++++++++++++ ExternalLibs/lvpa/ByteBuffer.h | 340 ++++++++++++++++++++ ExternalLibs/lvpa/ByteConverter.h | 46 +++ ExternalLibs/lvpa/LVPACommon.h | 44 +++ ExternalLibs/lvpa/LVPACompileConfig.h | 28 ++ ExternalLibs/lvpa/LVPAInternal.h | 201 ++++++++++++ ExternalLibs/ttvfs/CMakeLists.txt | 64 ++++ ExternalLibs/ttvfs/VFS.h | 78 +++++ ExternalLibs/ttvfs/VFSAtomic.cpp | 103 ++++++ ExternalLibs/ttvfs/VFSAtomic.h | 39 +++ ExternalLibs/ttvfs/VFSDefines.h | 79 +++++ ExternalLibs/ttvfs/VFSDir.cpp | 255 +++++++++++++++ ExternalLibs/ttvfs/VFSDir.h | 118 +++++++ ExternalLibs/ttvfs/VFSFile.cpp | 258 +++++++++++++++ ExternalLibs/ttvfs/VFSFile.h | 183 +++++++++++ ExternalLibs/ttvfs/VFSHelper.cpp | 350 ++++++++++++++++++++ ExternalLibs/ttvfs/VFSHelper.h | 192 +++++++++++ ExternalLibs/ttvfs/VFSInternal.h | 43 +++ ExternalLibs/ttvfs/VFSLoader.cpp | 102 ++++++ ExternalLibs/ttvfs/VFSLoader.h | 31 ++ ExternalLibs/ttvfs/VFSSelfRefCounter.h | 44 +++ ExternalLibs/ttvfs/VFSTools.cpp | 427 +++++++++++++++++++++++++ ExternalLibs/ttvfs/VFSTools.h | 59 ++++ 56 files changed, 4021 insertions(+), 606 deletions(-) create mode 100644 BBGE/VFSFileStream.cpp create mode 100644 BBGE/VFSFileStream.h create mode 100644 BBGE/VFSIncludes.h create mode 100644 ExternalLibs/lvpa/ByteBuffer.h create mode 100644 ExternalLibs/lvpa/ByteConverter.h create mode 100644 ExternalLibs/lvpa/LVPACommon.h create mode 100644 ExternalLibs/lvpa/LVPACompileConfig.h create mode 100644 ExternalLibs/lvpa/LVPAInternal.h create mode 100644 ExternalLibs/ttvfs/CMakeLists.txt create mode 100644 ExternalLibs/ttvfs/VFS.h create mode 100644 ExternalLibs/ttvfs/VFSAtomic.cpp create mode 100644 ExternalLibs/ttvfs/VFSAtomic.h create mode 100644 ExternalLibs/ttvfs/VFSDefines.h create mode 100644 ExternalLibs/ttvfs/VFSDir.cpp create mode 100644 ExternalLibs/ttvfs/VFSDir.h create mode 100644 ExternalLibs/ttvfs/VFSFile.cpp create mode 100644 ExternalLibs/ttvfs/VFSFile.h create mode 100644 ExternalLibs/ttvfs/VFSHelper.cpp create mode 100644 ExternalLibs/ttvfs/VFSHelper.h create mode 100644 ExternalLibs/ttvfs/VFSInternal.h create mode 100644 ExternalLibs/ttvfs/VFSLoader.cpp create mode 100644 ExternalLibs/ttvfs/VFSLoader.h create mode 100644 ExternalLibs/ttvfs/VFSSelfRefCounter.h create mode 100644 ExternalLibs/ttvfs/VFSTools.cpp create mode 100644 ExternalLibs/ttvfs/VFSTools.h diff --git a/Aquaria/Continuity.cpp b/Aquaria/Continuity.cpp index b99b7e2..1b3bc43 100644 --- a/Aquaria/Continuity.cpp +++ b/Aquaria/Continuity.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ScriptedEntity.h" #include "AutoMap.h" #include "GridRender.h" +#include #include "tinyxml.h" @@ -870,7 +871,7 @@ void Continuity::loadTreasureData() std::string line, gfx; int num, use; float sz; - std::ifstream in2("data/treasures.txt"); + VFSTextStdStreamIn in2("data/treasures.txt"); while (std::getline(in2, line)) { std::istringstream is(line); @@ -903,7 +904,7 @@ void Continuity::loadIngredientData() /* int num; - std::ifstream in2("data/ingredientdescriptions.txt"); + VFSTextStreamIn in2("data/ingredientdescriptions.txt"); while (std::getline(in2, line)) { IngredientDescription desc; @@ -916,7 +917,7 @@ void Continuity::loadIngredientData() clearIngredientData(); recipes.clear(); - std::ifstream in("data/ingredients.txt"); + VFSTextStdStreamIn in("data/ingredients.txt"); bool recipes = false; while (std::getline(in, line)) @@ -1241,7 +1242,7 @@ void Continuity::loadEatBank() { eats.clear(); - std::ifstream inf("data/eats.txt"); + VFSTextStdStreamIn inf("data/eats.txt"); EatData curData; std::string read; @@ -2181,7 +2182,7 @@ void Continuity::setActivePet(int flag) void Continuity::loadPetData() { petData.clear(); - std::ifstream in("data/pets.txt"); + VFSTextStdStreamIn in("data/pets.txt"); std::string read; while (std::getline(in, read)) { @@ -3259,7 +3260,7 @@ void Continuity::reset() health = maxHealth; speedTypes.clear(); - std::ifstream inFile("data/speedtypes.txt"); + VFSTextStreamIn inFile("data/speedtypes.txt"); int n, spd; while (inFile >> n) { diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index 3156fd4..e26372e 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -172,28 +172,6 @@ DSQ::DSQ(std::string fileSystem) : Core(fileSystem, LR_MAX, APPNAME, PARTICLE_AM almb = armb = 0; bar_left = bar_right = bar_up = bar_down = barFade_left = barFade_right = 0; - // do copy stuff -#ifdef BBGE_BUILD_UNIX - std::string fn; - fn = getPreferencesFolder() + "/" + userSettingsFilename; - if (!exists(fn)) - Linux_CopyTree(core->adjustFilenameCase(userSettingsFilename).c_str(), core->adjustFilenameCase(fn).c_str()); - - fn = getUserDataFolder() + "/_mods"; - if (!exists(fn)) - Linux_CopyTree(core->adjustFilenameCase("_mods").c_str(), core->adjustFilenameCase(fn).c_str()); -#endif - - -#if defined(BBGE_BUILD_UNIX) - std::string p1 = getUserDataFolder(); - std::string p2 = getUserDataFolder() + "/save"; - mkdir(p1.c_str(), S_IRWXU); - mkdir(p2.c_str(), S_IRWXU); - - //debugLogPath = ; -#endif - difficulty = DIFF_NORMAL; /* @@ -231,9 +209,6 @@ DSQ::DSQ(std::string fileSystem) : Core(fileSystem, LR_MAX, APPNAME, PARTICLE_AM achievement_box = 0; #endif - vars = &v; - v.load(); - #ifdef AQUARIA_BUILD_CONSOLE console = 0; #endif @@ -255,25 +230,6 @@ DSQ::DSQ(std::string fileSystem) : Core(fileSystem, LR_MAX, APPNAME, PARTICLE_AM for (int i = 0; i < 16; i++) firstElementOnLayer[i] = 0; - addStateInstance(game = new Game); - addStateInstance(new GameOver); -#ifdef AQUARIA_BUILD_SCENEEDITOR - addStateInstance(new AnimationEditor); -#endif - addStateInstance(new Intro2); - addStateInstance(new BitBlotLogo); -#ifdef AQUARIA_BUILD_SCENEEDITOR - addStateInstance(new ParticleEditor); -#endif - addStateInstance(new Credits); - addStateInstance(new Intro); - addStateInstance(new Nag); - - //addStateInstance(new Logo); - //addStateInstance(new SCLogo); - //addStateInstance(new IntroText); - //addStateInstance(new Intro); - //stream = 0; } @@ -343,7 +299,7 @@ void DSQ::newGame() void DSQ::loadElementEffects() { - std::ifstream inFile("data/elementeffects.txt"); + VFSTextStdStreamIn inFile("data/elementeffects.txt"); elementEffects.clear(); std::string line; while (std::getline(inFile, line)) @@ -915,6 +871,55 @@ static bool sdlVideoModeOK(const int w, const int h, const int bpp) void DSQ::init() { + setupVFS(getenv("AQUARIA_DATA_PATH")); + + // FG: TODO: do the moving & copying below with VFS code, and leave the original file system alone! + + // do copy stuff +#ifdef BBGE_BUILD_UNIX + std::string fn; + fn = getPreferencesFolder() + "/" + userSettingsFilename; + if (!exists(fn)) + Linux_CopyTree(core->adjustFilenameCase(userSettingsFilename).c_str(), core->adjustFilenameCase(fn).c_str()); + + fn = getUserDataFolder() + "/_mods"; + if (!exists(fn)) + Linux_CopyTree(core->adjustFilenameCase("_mods").c_str(), core->adjustFilenameCase(fn).c_str()); +#endif + + +#if defined(BBGE_BUILD_UNIX) + std::string p1 = getUserDataFolder(); + std::string p2 = getUserDataFolder() + "/save"; + mkdir(p1.c_str(), S_IRWXU); + mkdir(p2.c_str(), S_IRWXU); + + //debugLogPath = ; +#endif + + vars = &v; + v.load(); + + addStateInstance(game = new Game); + addStateInstance(new GameOver); +#ifdef AQUARIA_BUILD_SCENEEDITOR + addStateInstance(new AnimationEditor); +#endif + addStateInstance(new Intro2); + addStateInstance(new BitBlotLogo); +#ifdef AQUARIA_BUILD_SCENEEDITOR + addStateInstance(new ParticleEditor); +#endif + addStateInstance(new Credits); + addStateInstance(new Intro); + addStateInstance(new Nag); + + //addStateInstance(new Logo); + //addStateInstance(new SCLogo); + //addStateInstance(new IntroText); + //addStateInstance(new Intro); + + core->settings.runInBackground = true; weird = 0; @@ -2165,6 +2170,13 @@ ModEntry* DSQ::getSelectedModEntry() void DSQ::loadMods() { modEntries.clear(); + + // force VFS to reload _mods dir + if(ttvfs::VFSDir *vd = vfs.GetDir("_mods")) + { + vd->load(); + vfs.Reload(); + } forEachFile(mod.getBaseModPath(), ".xml", loadModsCallback, 0); selectedMod = 0; @@ -3675,7 +3687,7 @@ void DSQ::onPlayVoice() if (user.audio.subtitles) { std::string fn = "scripts/vox/" + sound->lastVoice + ".txt"; - std::ifstream inf(fn.c_str()); + VFSTextStdStreamIn inf(fn.c_str()); if (inf.is_open()) { std::string dia; @@ -3768,31 +3780,6 @@ std::string DSQ::getDialogueFilename(const std::string &f) return "dialogue/" + languagePack + "/" + f + ".txt"; } -void DSQ::jumpToSection(std::ifstream &inFile, const std::string §ion) -{ - if (section.empty()) return; - std::string file = dsq->getDialogueFilename(dialogueFile); - if (!exists(file)) - { - debugLog("Could not find dialogue [" + file + "]"); - return; - } - inFile.open(core->adjustFilenameCase(file).c_str()); - std::string s; - while (std::getline(inFile, s)) - { - if (!s.empty()) - { - if (s.find("[")!=std::string::npos && s.find(section) != std::string::npos) - { - return; - } - } - } - debugLog("could not find section [" + section + "]"); -} - - void DSQ::runGesture(const std::string &line) { std::istringstream is(line); diff --git a/Aquaria/DSQ.h b/Aquaria/DSQ.h index 6e300a7..0e81ca0 100644 --- a/Aquaria/DSQ.h +++ b/Aquaria/DSQ.h @@ -1395,8 +1395,6 @@ public: void takeScreenshot(); void takeScreenshotKey(); - void jumpToSection(std::ifstream &inFile, const std::string §ion); - PathFinding pathFinding; void runGesture(const std::string &line); void generateCollisionMask(RenderObject *r); diff --git a/Aquaria/Emote.cpp b/Aquaria/Emote.cpp index 5ef43b4..4a3ed67 100644 --- a/Aquaria/Emote.cpp +++ b/Aquaria/Emote.cpp @@ -31,7 +31,7 @@ Emote::Emote() void Emote::load(const std::string &file) { emotes.clear(); - std::ifstream in(file.c_str()); + VFSTextStdStreamIn in(file.c_str()); std::string line; while (std::getline(in, line)) diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index a6da240..bc3edad 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -39,6 +39,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "StatsAndAchievements.h" #include "ToolTip.h" +#include std::vector allowedMaps; @@ -253,7 +254,7 @@ void FoodHolder::setIngredient(IngredientData *i, bool effects) if (effects) { core->sound->playSfx("Wok"); - + ing->scale.ensureData(); ing->scale.data->path.clear(); ing->scale.data->path.addPathNode(Vector(1,1),0); @@ -2516,7 +2517,7 @@ void Game::loadEntityTypeList() // and group list! { entityTypeList.clear(); - std::ifstream in("scripts/entities/entities.txt"); + VFSTextStdStreamIn in("scripts/entities/entities.txt"); std::string line; if(!in) { @@ -2549,7 +2550,7 @@ void Game::loadEntityTypeList() fn = dsq->mod.getPath() + "entitygroups.txt"; } - std::ifstream in2(fn.c_str()); + VFSTextStdStreamIn in2(fn.c_str()); int curGroup=0; while (std::getline(in2, line)) @@ -5403,7 +5404,7 @@ void Game::findMaxCameraValues() void Game::setWarpAreaSceneName(WarpArea &warpArea) { - std::ifstream in("data/warpAreas.txt"); + VFSTextStdStreamIn in("data/warpAreas.txt"); std::string color, area1, dir1, area2, dir2; std::string line; while (std::getline(in, line)) @@ -7933,9 +7934,9 @@ void Game::onFlipTest() void appendFileToString(std::string &string, const std::string &file) { - std::ifstream inf(file.c_str()); + VFSTextStdStreamIn inf(file.c_str()); - if (inf.is_open()) + if (inf) { while (!inf.eof()) { @@ -10945,7 +10946,7 @@ void Game::loadElementTemplates(std::string pack) tileCache.clean(); } - std::ifstream in(fn.c_str()); + VFSTextStdStreamIn in(fn.c_str()); std::string line; while (std::getline(in, line)) { diff --git a/Aquaria/GameplayVariables.cpp b/Aquaria/GameplayVariables.cpp index a938b7f..bdbc3dd 100644 --- a/Aquaria/GameplayVariables.cpp +++ b/Aquaria/GameplayVariables.cpp @@ -24,12 +24,13 @@ GameplayVariables *vars = 0; void GameplayVariables::load() { - std::ifstream inFile("data/variables.txt"); + VFSTextStreamIn inFile("data/variables.txt"); if(!inFile) { core->messageBox("error", "Variables data not found! Aborting..."); exit(1); } + std::string s; inFile >> s >> maxSlowSwimSpeed; inFile >> s >> maxSwimSpeed; diff --git a/Aquaria/Main.cpp b/Aquaria/Main.cpp index d64d9dc..6922eb6 100644 --- a/Aquaria/Main.cpp +++ b/Aquaria/Main.cpp @@ -41,10 +41,10 @@ static void StartAQConfig() { #if defined(BBGE_BUILD_WINDOWS) #if defined(AQUARIA_DEMO) || defined(AQUARIA_FULL) - if (!exists("ran", false)) + if (!exists("ran", false, true)) { MakeRan(); - if(exists("aqconfig.exe", false)) + if(exists("aqconfig.exe", false, true)) { ShellExecute(NULL, "open", "aqconfig.exe", NULL, NULL, SW_SHOWNORMAL); exit(0); @@ -58,7 +58,7 @@ static void StartAQConfig() static void CheckConfig(void) { #ifdef BBGE_BUILD_WINDOWS - bool hasCfg = exists("usersettings.xml", false); + bool hasCfg = exists("usersettings.xml", false, true); if(!hasCfg) StartAQConfig(); #endif diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 76dae95..7c66794 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -529,7 +529,7 @@ luaFunc(indexWarnGlobal) std::ostringstream os; os << "WARNING: " << ar.short_src << ":" << ar.currentline << ": script tried to get/call undefined global variable " - << lua_tostring(L, -2); + << varname; errorLog(os.str()); } @@ -614,7 +614,19 @@ luaFunc(dofile_caseinsensitive) // This is Lua's dofile(), with some tweaks. --ryan. std::string fname(core->adjustFilenameCase(luaL_checkstring(L, 1))); int n = lua_gettop(L); - if (luaL_loadfile(L, fname.c_str()) != 0) lua_error(L); + + int result = -1; + ttvfs::VFSFile *vf = core->vfs.GetFile(fname.c_str()); + if(vf) + { + const char *buf = (const char*)vf->getBuf(); + result = luaL_loadbuffer(L, buf, vf->size(), fname.c_str()); + vf->dropBuf(true); + } + + if(result) + lua_error(L); + lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - n; } @@ -8941,11 +8953,20 @@ Script *ScriptInterface::openScript(const std::string &file) lua_getglobal(baseState, "v"); // Load the file itself. This leaves the Lua chunk on the stack. - int result = luaL_loadfile(baseState, realFile.c_str()); + int result = -1; + ttvfs::VFSFile *vf = core->vfs.GetFile(realFile.c_str()); + if(vf) + { + const char *buf = (const char*)vf->getBuf(); + result = luaL_loadbuffer(baseState, buf, vf->size(), realFile.c_str()); + vf->dropBuf(true); + } + if (result != 0) { - debugLog("Error loading script [" + realFile + "]: " + lua_tostring(baseState, -1)); - lua_pop(baseState, 2); + const char *msg = lua_tostring(baseState, -1); + debugLog("Error loading script [" + realFile + "]: " + (msg ? msg : "unk error")); // loading from buffer does not push a string on the stack + //lua_pop(baseState, 2); return NULL; } diff --git a/Aquaria/Shot.cpp b/Aquaria/Shot.cpp index 8d70893..e5a21ed 100644 --- a/Aquaria/Shot.cpp +++ b/Aquaria/Shot.cpp @@ -25,6 +25,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "../BBGE/MathFunctions.h" +#include + Shot::Shots Shot::shots; Shot::ShotBank Shot::shotBank; @@ -64,7 +66,7 @@ ShotData::ShotData() ignoreShield = false; } -void readEquals2(std::ifstream &in) +template void readEquals2(T &in) { std::string temp; in >> temp; @@ -93,7 +95,7 @@ void ShotData::bankLoad(const std::string &file, const std::string &path) } debugLog(usef); - std::ifstream inf(usef.c_str()); + VFSTextStreamIn inf(usef.c_str()); std::string token; while (inf >> token) { diff --git a/Aquaria/StatsAndAchievements.cpp b/Aquaria/StatsAndAchievements.cpp index c0c72e1..1266b6d 100644 --- a/Aquaria/StatsAndAchievements.cpp +++ b/Aquaria/StatsAndAchievements.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Game.h" #include "Avatar.h" #include "StatsAndAchievements.h" +#include #ifndef ARRAYSIZE #define ARRAYSIZE(x) (sizeof (x) / sizeof ((x)[0])) @@ -171,46 +172,50 @@ void StatsAndAchievements::RunFrame() requestedStats = true; const size_t max_achievements = ARRAYSIZE(g_rgAchievements); - FILE *io = NULL; - // Get generic achievement data... - io = fopen("data/achievements.txt", "r"); - char line[1024]; - for (size_t i = 0; i < max_achievements; i++) - { - if (!io || (fgets(line, sizeof (line), io) == NULL)) - snprintf(line, sizeof (line), "Achievement #%d", (int) i); - else - { - for (char *ptr = (line + strlen(line)) - 1; (ptr >= line) && ((*ptr == '\r') || (*ptr == '\n')); ptr--) - *ptr = '\0'; - } - line[sizeof (g_rgAchievements[i].name) - 1] = '\0'; // just in case. - strcpy(g_rgAchievements[i].name, line); + char *achtxt = ""; + VFSTextStdStreamIn in("data/achievements.txt"); + std::string line; - if (!io || (fgets(line, sizeof (line), io) == NULL)) - snprintf(line, sizeof (line), "[Description of Achievement #%d is missing!]", (int) i); - else - { - for (char *ptr = (line + strlen(line)) - 1; (ptr >= line) && ((*ptr == '\r') || (*ptr == '\n')); ptr--) - *ptr = '\0'; - } - line[sizeof (g_rgAchievements[i].desc) - 1] = '\0'; // just in case. - strcpy(g_rgAchievements[i].desc, line); + // HACK: prepare fields in case data are missing + for(int i = 0; i < max_achievements; ++i) + { + g_rgAchievements[i].iconImage = 0; + g_rgAchievements[i].name[sizeof (g_rgAchievements[i].name) - 1] = '\0'; // just in case. + g_rgAchievements[i].desc[sizeof (g_rgAchievements[i].desc) - 1] = '\0'; + snprintf(g_rgAchievements[i].name, sizeof(g_rgAchievements[i].name), "Achievement #%d", i); + snprintf(g_rgAchievements[i].desc, sizeof(g_rgAchievements[i].desc), "[Description of Achievement #%d is missing!]", i); + } - // unsupported at the moment. - g_rgAchievements[i].iconImage = 0; - } + // read 2 lines per achievement + int x = 0; + int ach = 0; + while(std::getline(in, line)) + { + for (char *ptr = const_cast(line.c_str() + line.length()) - 1; (ptr >= line) && ((*ptr == '\r') || (*ptr == '\n')); ptr--) + *ptr = '\0'; - if (io != NULL) - fclose(io); + switch(x) + { + case 0: + strncpy(g_rgAchievements[ach].name, line.c_str(), sizeof(g_rgAchievements[ach].name) - 1); + ++x; + break; + + case 1: + strncpy(g_rgAchievements[ach].desc, line.c_str(), sizeof(g_rgAchievements[ach].desc) - 1); + x = 0; + ++ach; + } + } // See what this specific player has achieved... + // FG: TODO: use VFS here! unsigned char *buf = new unsigned char[max_achievements]; size_t br = 0; const std::string fname(core->getUserDataFolder() + "/achievements.bin"); - io = fopen(fname.c_str(), "rb"); + FILE *io = fopen(fname.c_str(), "rb"); if (io == NULL) statsValid = true; // nothing to report. else diff --git a/Aquaria/StringBank.cpp b/Aquaria/StringBank.cpp index a37cb5b..25671a7 100644 --- a/Aquaria/StringBank.cpp +++ b/Aquaria/StringBank.cpp @@ -29,7 +29,7 @@ void StringBank::load(const std::string &file) //debugLog("StringBank::load("+file+")"); stringMap.clear(); - std::ifstream in(file.c_str()); + VFSTextStdStreamIn in(file.c_str()); std::string line; int idx; diff --git a/Aquaria/SubtitlePlayer.cpp b/Aquaria/SubtitlePlayer.cpp index 75ed84c..20128a6 100644 --- a/Aquaria/SubtitlePlayer.cpp +++ b/Aquaria/SubtitlePlayer.cpp @@ -61,7 +61,7 @@ void SubtitlePlayer::go(const std::string &subs) } } - std::ifstream in(f.c_str()); + VFSTextStdStreamIn in(f.c_str()); std::string line; while (std::getline(in, line)) { diff --git a/Aquaria/WorldMapTiles.cpp b/Aquaria/WorldMapTiles.cpp index f535a44..f2051a3 100644 --- a/Aquaria/WorldMapTiles.cpp +++ b/Aquaria/WorldMapTiles.cpp @@ -246,7 +246,7 @@ void WorldMap::load(const std::string &file) std::string line; - std::ifstream in(file.c_str()); + VFSTextStdStreamIn in(file.c_str()); while (std::getline(in, line)) { @@ -261,6 +261,7 @@ void WorldMap::load(const std::string &file) void WorldMap::save(const std::string &file) { + // FG: TODO: use VFS here! std::ofstream out(file.c_str()); for (int i = 0; i < worldMapTiles.size(); i++) diff --git a/BBGE/Base.cpp b/BBGE/Base.cpp index 049023b..7a7dec8 100644 --- a/BBGE/Base.cpp +++ b/BBGE/Base.cpp @@ -20,6 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Base.h" #include "Core.h" +#include "VFSDir.h" #ifdef BBGE_BUILD_WINDOWS #include @@ -272,30 +273,28 @@ std::string upperCase(const std::string &s1) return ret; } -bool exists(const std::string &f, bool makeFatal) +bool exists(const std::string &f, bool makeFatal /* = false */, bool skipVFS /* = false */) { - /* - if (!PHYSFS_exists(f.c_str())) - { - */ - /* - std::ostringstream os; - os << "checking to see if [" << f << "] exists"; - debugLog(os.str()); - */ + if (f.empty()) + return false; - FILE *file = fopen(core->adjustFilenameCase(f).c_str(), "rb"); - if (!file) + if(!skipVFS) + { + if(core->vfs.GetFile(core->adjustFilenameCase(f).c_str())) + return true; + } + + FILE *file = fopen(core->adjustFilenameCase(f).c_str(), "rb"); + if (!file) + { + if (makeFatal) { - if (makeFatal) - { - errorLog(std::string("Could not open [" + f + "]")); - exit(0); - } - return false; + errorLog(std::string("Could not open [" + f + "]")); + exit(0); } - fclose(file); - //} + return false; + } + fclose(file); return true; } @@ -448,107 +447,20 @@ void debugLog(const std::string &s) // delete[] when no longer needed. char *readFile(std::string path, unsigned long *size_ret) { - FILE *f = fopen(path.c_str(), "rb"); - if (!f) - return NULL; - - long fileSize; - if (fseek(f, 0, SEEK_END) != 0 - || (fileSize = ftell(f)) < 0 - || fseek(f, 0, SEEK_SET) != 0) - { - debugLog(path + ": Failed to get file size"); - fclose(f); - return NULL; - } - - char *buffer = new char[fileSize + 1]; - if (!buffer) - { - std::ostringstream os; - os << path << ": Not enough memory for file (" - << (fileSize+1) << " bytes)"; - debugLog(os.str()); - fclose(f); - return NULL; - } - - long bytesRead = fread(buffer, 1, fileSize, f); - if (bytesRead != fileSize) - { - std::ostringstream os; - os << path << ": Failed to read file (only got " - << bytesRead << " of " << fileSize << " bytes)"; - debugLog(os.str()); - fclose(f); - return NULL; - } - - fclose(f); - if (size_ret) - *size_ret = fileSize; - buffer[fileSize] = 0; - return buffer; -} - -/* -void pForEachFile(std::string path, std::string type, void callback(const std::string &filename, int param), int param) -{ - char **rc = PHYSFS_enumerateFiles(path.c_str()); - char **i; - - for (i = rc; *i != NULL; i++) - { - std::string s(*i); - int p=0; - if ((p=s.find('.'))!=std::string::npos) - { - std::string ext = s.susbtr(p, s.getLength2D()); - if (ext == type) - { - callback(fielnameafhghaha - } - } - } - - PHYSFS_freeList(rc); -} -*/ - -void doSingleFile(const std::string &path, const std::string &type, std::string filename, void callback(const std::string &filename, int param), int param) -{ - if (filename.size()>4) - { - std::string search = filename; - stringToLower(search); - std::string filetype = filename.substr(search.size()-4, search.size()); - //stringToUpper(filetype); - //debugLog("comparing: " + filetype + " and: " + type); - //if (filetype==type) - debugLog("checking:" + search + " for type:" + type); - if (search.find(type)!=std::string::npos) - { - debugLog("callback"); - callback(path+filename, param); - } - else - { - debugLog("not the same"); - } - } -} - -std::string stripEndlineForUnix(const std::string &in) -{ - std::string out; - for (int i = 0; i < in.size(); i++) - { - if (int(in[i]) != 13) - { - out+= in[i]; - } - } - return out; + ttvfs::VFSFile *vf = core->vfs.GetFile(path.c_str()); + if(!vf) + return NULL; + vf->getBuf(); // force size calc early + // we can never know how the memory was allocated; + // because the buffer is expected to be deleted with delete[], + // it has to be explicitly copied to memory allocated with new[]. + unsigned long s = vf->size(); + char *buf = new char[s + 1]; + memcpy(buf, vf->getBuf(), s + 1); + core->addVFSFileForDrop(vf); + if(size_ret) + *size_ret = s; + return buf; } void forEachFile(std::string path, std::string type, void callback(const std::string &filename, intptr_t param), intptr_t param) @@ -560,167 +472,29 @@ void forEachFile(std::string path, std::string type, void callback(const std::st //HACK: MAC: debugLog("forEachFile - path: " + path + " type: " + type); -#if defined(BBGE_BUILD_UNIX) - DIR *dir=0; - dir = opendir(path.c_str()); - if (dir) - { - dirent *file=0; - while ( (file=readdir(dir)) != NULL ) - { - if (file->d_name && strlen(file->d_name) > 4) - { - debugLog(file->d_name); - char *extension=strrchr(file->d_name,'.'); - if (extension) - { - debugLog(extension); - if (extension!=NULL) - { - if (strcasecmp(extension,type.c_str())==0) - { - callback(path + std::string(file->d_name), param); - } - } - } - } - } - closedir(dir); - } - else - { - debugLog("FAILED TO OPEN DIR"); - } -#endif - -#ifdef BBGE_BUILD_WINDOWS - BOOL fFinished; - HANDLE hList; - TCHAR szDir[MAX_PATH+1]; - WIN32_FIND_DATA FileData; - - int end = path.size()-1; - if (path[end] != '/') - path[end] += '/'; - - // Get the proper directory path - // \\ %s\\* - - - - if (type.find('.')==std::string::npos) - { - type = "." + type; - } - - - //std::string add = "%s*" + type; - - //sprintf(szDir, "%s*", path.c_str()); - sprintf(szDir, "%s\\*", path.c_str()); - - stringToUpper(type); - - // Get the first file - hList = FindFirstFile(szDir, &FileData); - if (hList == INVALID_HANDLE_VALUE) + ttvfs::VFSDir *vd = core->vfs.GetDir(path.c_str(), false); + if(!vd) { - //printf("No files found\n\n"); - debugLog("No files of type " + type + " found in path " + path); + debugLog("Path '" + path + "' does not exist"); + return; } - else + + for(ttvfs::ConstFileIter it = vd->fileIter(); it != vd->fileIterEnd(); ++it) { - // Traverse through the directory structure - fFinished = FALSE; - while (!fFinished) + const ttvfs::VFSFile *f = it->second; + const char *e = strrchr(f->name(), '.'); + if (e) { - // Check the object is a directory or not - //printf("%*s%s\n", indent, "", FileData.cFileName); - std::string filename = FileData.cFileName; - //debugLog("found: " + filename); - if (filename.size()>4) - { - - std::string filetype = filename.substr(filename.size()-4, filename.size()); - stringToUpper(filetype); - //debugLog("comparing: " + filetype + " and: " + type); - if (filetype==type) - { - callback(path+filename, param); - } - } - - - if (!FindNextFile(hList, &FileData)) - { - /* - if (GetLastError() == ERROR_NO_MORE_FILES) - { - fFinished = TRUE; - } - */ - fFinished = TRUE; - } + std::string exs(e); + stringToLower(exs); + if(exs != type) + continue; } + else if(type.size()) + continue; + + callback(path + f->name(), param); } - - FindClose(hList); -#endif -} - -std::vector getFileList(std::string path, std::string type, int param) -{ - std::vector list; - -#ifdef BBGE_BUILD_WINDOWS - BOOL fFinished; - HANDLE hList; - TCHAR szDir[MAX_PATH+1]; - WIN32_FIND_DATA FileData; - - // Get the proper directory path - sprintf(szDir, "%s\\*", path.c_str()); - - - // Get the first file - hList = FindFirstFile(szDir, &FileData); - if (hList == INVALID_HANDLE_VALUE) - { - printf("No files found\n\n"); - } - else - { - // Traverse through the directory structure - fFinished = FALSE; - while (!fFinished) - { - // Check the object is a directory or not - //printf("%*s%s\n", indent, "", FileData.cFileName); - std::string filename = FileData.cFileName; - if (filename.size()>4 && filename.substr(filename.size()-4, filename.size())==type) - { - //callback(path+filename, param); - list.push_back (filename); - } - - - if (!FindNextFile(hList, &FileData)) - { - /* - if (GetLastError() == ERROR_NO_MORE_FILES) - { - fFinished = TRUE; - } - */ - fFinished = TRUE; - } - } - } - - FindClose(hList); -#endif - - return list; } std::string msg(const std::string &message) diff --git a/BBGE/Base.h b/BBGE/Base.h index cef7e2c..459bdf7 100644 --- a/BBGE/Base.h +++ b/BBGE/Base.h @@ -192,13 +192,11 @@ void stringToLower(std::string &s); void stringToLowerUserData(std::string &s); void glColor3_256(int r, int g, int b); float sqr(float x); -bool exists(const std::string &f, bool makeFatal = false); +bool exists(const std::string &f, bool makeFatal = false, bool skipVFS = false); void errorLog(const std::string &s); void debugLog(const std::string &s); char *readFile(std::string path, unsigned long *size_ret = 0); void forEachFile(std::string path, std::string type, void callback(const std::string &filename, intptr_t param), intptr_t param); -std::string stripEndlineForUnix(const std::string &in); -std::vector getFileList(std::string path, std::string type, int param); #ifdef HAVE_STRCASECMP static inline int nocasecmp(const std::string &s1, const std::string &s2) { return strcasecmp(s1.c_str(), s2.c_str()); } @@ -225,19 +223,6 @@ Vector colorRGB(int r, int g, int b); #endif GLuint generateEmptyTexture(int res); -//void pForEachFile(std::string path, std::string type, void callback(const std::string &filename, int param), int param); - -/* -void pfread(void *buffer, PHYSFS_uint32 size, PHYSFS_uint32 objs, PHYSFS_file *handle); -void pfseek(PHYSFS_file *handle,PHYSFS_uint64 byte,int origin); -void pfclose(PHYSFS_file *handle); - - -PHYSFS_file *openRead(const std::string &f); -std::string pLoadStream(const std::string &filename); -void pSaveStream(const std::string &filename, std::ostringstream &os); -*/ - void drawCircle(float radius, int steps=1); bool isVectorInRect(const Vector &vec, const Vector &coord1, const Vector &coord2); diff --git a/BBGE/BitmapFont.cpp b/BBGE/BitmapFont.cpp index 10943bb..ecac114 100644 --- a/BBGE/BitmapFont.cpp +++ b/BBGE/BitmapFont.cpp @@ -100,7 +100,7 @@ void BitmapText::autoKern() void BitmapText::loadSpacingMap(const std::string &file) { spacingMap.clear(); - std::ifstream inFile(file.c_str()); + VFSTextStdStreamIn inFile(file.c_str()); std::string line; while (std::getline(inFile, line)) { diff --git a/BBGE/Core.cpp b/BBGE/Core.cpp index ce63582..23e8d60 100644 --- a/BBGE/Core.cpp +++ b/BBGE/Core.cpp @@ -4193,6 +4193,10 @@ void Core::shutdown() SDL_Quit(); debugLog("OK"); #endif + + debugLog("Unloading VFS..."); + vfs.Clear(); + debugLog("OK"); } //util funcs @@ -4528,6 +4532,33 @@ void Core::clearGarbage() i++; } + + // delete leftover buffers from VFS + // FG: TODO: better do that periodically, and not every loop? + for(std::set::iterator it = vfsFilesToClear.begin(); it != vfsFilesToClear.end(); ++it) + { + (*it)->dropBuf(true); + (*it)->ref--; + } + vfsFilesToClear.clear(); +} + +void Core::addVFSFileForDrop(ttvfs::VFSFile *vf) +{ + // HACK: because of save/poot.tmp caching and other stuff, we have to clear this always + // TODO: after getting rid of pack/unpackFile, this will be safer and can be done properly + if(strstr(vf->name(), "poot") || strstr(vf->name(), ".tmp")) + { + vf->dropBuf(true); + return; + } + + std::set::iterator it = vfsFilesToClear.find(vf); + if(it == vfsFilesToClear.end()) + { + vf->ref++; + vfsFilesToClear.insert(vf); + } } bool Core::canChangeState() @@ -4858,3 +4889,69 @@ int Core::tgaSaveSeries(char *filename, // ilutGLScreenie(); } +#include "VFSTools.h" + +static void _DumpVFS(const std::string a) +{ + std::string fn = "vfsdump-" + a + ".txt"; + std::ofstream out(fn.c_str()); + core->vfs.debugDumpTree(out); + out.close(); +} + +void Core::setupVFS(const char *extradir /* = NULL */) +{ + debugLog("Init VFS..."); + if(!ttvfs::checkCompat()) + { + errorLog("VFS incompatible!"); + exit(1); + } + vfs.LoadFileSysRoot(); + vfs.Prepare(); + +//#ifdef _DEBUG +// _DumpVFS("begin"); +//#endif + + +#ifdef BBGE_BUILD_UNIX + // Load _mods dir from home folder into the data of the existing _mods dir, additionally. + // This does also change the internal path, so that files created using this dir's fullname() will + // automatically be created in the home directory. + ttvfs::VFSDir *moddir = vfs.GetDir("_mods", true); + std::string umods = getUserDataFolder() + "/_mods"; + moddir->load(umods.c_str()); + + std::ostringstream os; + os << "VFS: Mounted _mods as: '" << vfs.GetDir("_mods")->fullname() << "'"; + debugLog(os.str()); + + // Note: the original code to load/save mods is not yet changed. + // This will happen in a later patch. +#endif + + if(extradir) + { + std::string msg("VFS extra dir: "); + msg += extradir; + debugLog(msg); + if(vfs.GetDir(extradir)) + vfs.Mount(extradir, ""); + else + vfs.MountExternalPath(extradir, ""); + debugLog("extra dir added."); + } + + // Example: everything in _patch dir will be mounted in the game's root dir + // -- place any files/folders there to override those the game uses. + // TODO: remove this later! - When the community datafile update is organized and everything. + vfs.Mount("_patch", "", true); + + + debugLog("VFS init done!"); + +//#ifdef _DEBUG +// _DumpVFS("done"); +//#endif +} diff --git a/BBGE/Core.h b/BBGE/Core.h index 6c7495e..b59795b 100644 --- a/BBGE/Core.h +++ b/BBGE/Core.h @@ -51,6 +51,8 @@ BUILD_LINUX #include "FrameBuffer.h" #include "Shader.h" +#include "VFSIncludes.h" + class ParticleEffect; class ParticleManager; @@ -1400,6 +1402,14 @@ protected: int tgaSave(const char *filename, short int width, short int height, unsigned char pixelDepth, unsigned char *imageData); virtual void onUpdate(float dt); virtual void onRender(){} + + // VFS related +private: + std::set vfsFilesToClear; // used for dropBuf() delaying +public: + ttvfs::VFSHelper vfs; + void setupVFS(const char *extradir = NULL); + void addVFSFileForDrop(ttvfs::VFSFile *vf); }; extern Core *core; diff --git a/BBGE/FmodOpenALBridge.cpp b/BBGE/FmodOpenALBridge.cpp index 5fc8f78..ba2dd30 100644 --- a/BBGE/FmodOpenALBridge.cpp +++ b/BBGE/FmodOpenALBridge.cpp @@ -34,6 +34,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Base.h" #include "Core.h" +#include "VFSFile.h" + #include "FmodOpenALBridge.h" #include "al.h" @@ -53,7 +55,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. class OggDecoder { public: // Create a decoder that streams from a file. - OggDecoder(FILE *fp); + OggDecoder(ttvfs::VFSFile *fp); // Create a decoder that streams from a memory buffer. OggDecoder(const void *data, long data_size); @@ -98,7 +100,7 @@ private: // Data source. If fp != NULL, the source is that file; otherwise, the // source is the buffer pointed to by "data" with size "data_size" bytes. - FILE *fp; + ttvfs::VFSFile *fp; const char *data; long data_size; long data_pos; // Current read position for memory buffers @@ -129,22 +131,38 @@ private: // ov_open_callbacks() call. Note that we rename the fseek() wrapper // to avoid an identifier collision when building with more recent // versions of libvorbis. -static int BBGE_ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){ +static int BBGE_ov_header_fseek_wrap(void *f,ogg_int64_t off,int whence){ if(f==NULL)return(-1); -#ifdef __MINGW32__ - return fseeko64(f,off,whence); -#elif defined (_WIN32) - return _fseeki64(f,off,whence); -#else - return fseek(f,off,whence); -#endif + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)f; + switch(whence) + { + case SEEK_SET: return vf->seek(off); + case SEEK_CUR: return vf->seekRel(off); + case SEEK_END: return vf->seek(vf->size() - off); + } + return -1; } -static int noclose(FILE *f) {return 0;} +static size_t BBGE_ov_fread_wrap(void *ptr, size_t s, size_t count, void *f) +{ + if(f==NULL)return(-1); + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)f; + size_t done = vf->read(ptr, s * count); + return done / s; +} + +static long BBGE_ov_ftell_wrap(void *f) +{ + if(f==NULL)return(-1); + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)f; + return vf->getpos(); +} + +static int noclose(void *f) {return 0;} static const ov_callbacks local_OV_CALLBACKS_NOCLOSE = { - (size_t (*)(void *, size_t, size_t, void *)) fread, + (size_t (*)(void *, size_t, size_t, void *)) BBGE_ov_fread_wrap, (int (*)(void *, ogg_int64_t, int)) BBGE_ov_header_fseek_wrap, (int (*)(void *)) noclose, // NULL doesn't work in libvorbis-1.1.2 - (long (*)(void *)) ftell + (long (*)(void *)) BBGE_ov_ftell_wrap }; // Memory I/O callback set. @@ -156,7 +174,7 @@ static const ov_callbacks ogg_memory_callbacks = { }; -OggDecoder::OggDecoder(FILE *fp) +OggDecoder::OggDecoder(ttvfs::VFSFile *fp) { for (int i = 0; i < NUM_BUFFERS; i++) { @@ -517,9 +535,9 @@ static ALenum GVorbisFormat = AL_NONE; class OpenALSound { public: - OpenALSound(FILE *_fp, const bool _looping); + OpenALSound(ttvfs::VFSFile *_fp, const bool _looping); OpenALSound(void *_data, long _size, const bool _looping); - FILE *getFile() const { return fp; } + ttvfs::VFSFile *getFile() const { return fp; } const void *getData() const { return data; } long getSize() const { return size; } bool isLooping() const { return looping; } @@ -527,20 +545,21 @@ public: void reference() { refcount++; } private: - FILE * const fp; + ttvfs::VFSFile * const fp; void * const data; // Only used if fp==NULL const long size; // Only used if fp==NULL const bool looping; int refcount; }; -OpenALSound::OpenALSound(FILE *_fp, const bool _looping) +OpenALSound::OpenALSound(ttvfs::VFSFile *_fp, const bool _looping) : fp(_fp) , data(NULL) , size(0) , looping(_looping) , refcount(1) { + fp->ref++; } OpenALSound::OpenALSound(void *_data, long _size, const bool _looping) @@ -559,7 +578,11 @@ FMOD_RESULT OpenALSound::release() if (refcount <= 0) { if (fp) - fclose(fp); + { + fp->close(); + fp->dropBuf(true); // just in case there is a buffer... + fp->ref--; + } else free(data); delete this; @@ -1044,8 +1067,6 @@ FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE { assert(!exinfo); - FMOD_RESULT retval = FMOD_ERR_INTERNAL; - // !!! FIXME: if it's not Ogg, we don't have a decoder. I'm lazy. :/ char *fname = (char *) alloca(strlen(name_or_data) + 16); strcpy(fname, name_or_data); @@ -1053,50 +1074,39 @@ FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE if (ptr) *ptr = '\0'; strcat(fname, ".ogg"); - // just in case... - #undef fopen - FILE *io = fopen(core->adjustFilenameCase(fname).c_str(), "rb"); - if (io == NULL) + ttvfs::VFSFile *vf = core->vfs.GetFile(fname); + if(!vf) return FMOD_ERR_INTERNAL; - if (mode & FMOD_CREATESTREAM) + if(mode & FMOD_CREATESTREAM) { - *sound = (Sound *) new OpenALSound(io, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); - retval = FMOD_OK; - } - else - { - fseek(io, 0, SEEK_END); - long size = ftell(io); - if (fseek(io, 0, SEEK_SET) != 0) - { - debugLog("Seek error on " + std::string(fname)); - fclose(io); - return FMOD_ERR_INTERNAL; - } + // does it make sense to try to stream from anything else than an actual file on disk? + // Files inside containers are always loaded into memory, unless on-the-fly partial decompression is implemented... + // A typical ogg is < 3 MB in size, if that is preloaded and then decoded over time it should still be a big gain. + if(!vf->isopen()) + vf->open(NULL, "rb"); + else + vf->seek(0); - void *data = malloc(size); - if (data == NULL) - { - debugLog("Out of memory for " + std::string(fname)); - fclose(io); - return FMOD_ERR_INTERNAL; - } - - long nread = fread(data, 1, size, io); - fclose(io); - if (nread != size) - { - debugLog("Failed to read data from " + std::string(fname)); - free(data); - return FMOD_ERR_INTERNAL; - } - - *sound = (Sound *) new OpenALSound(data, size, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); - retval = FMOD_OK; + *sound = (Sound *) new OpenALSound(vf, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); + return FMOD_OK; } - return retval; + // if we are here, create & preload & pre-decode full buffer + vf->getBuf(); // force early size detection + void *data = malloc(vf->size()); // because release() will use free() ... + if (!(data && vf->getBuf())) + { + debugLog("Out of memory for " + std::string(fname)); + vf->close(); + vf->dropBuf(true); + return FMOD_ERR_INTERNAL; + } + memcpy(data, vf->getBuf(), vf->size()); + core->addVFSFileForDrop(vf); + *sound = (Sound *) new OpenALSound(data, vf->size(), (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); + + return FMOD_OK; } ALBRIDGE(System,createStream,(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound),(name_or_data,mode,exinfo,sound)) diff --git a/BBGE/Precacher.cpp b/BBGE/Precacher.cpp index eb315d9..78eff0e 100644 --- a/BBGE/Precacher.cpp +++ b/BBGE/Precacher.cpp @@ -123,7 +123,7 @@ void Precacher::precacheTex(const std::string &tex) void Precacher::precacheList(const std::string &list, void progressCallback()) { loadProgressCallback = progressCallback; - std::ifstream in(list.c_str()); + VFSTextStdStreamIn in(list.c_str()); std::string t; while (std::getline(in, t)) { diff --git a/BBGE/Shader.cpp b/BBGE/Shader.cpp index bb441e1..5dbf64b 100644 --- a/BBGE/Shader.cpp +++ b/BBGE/Shader.cpp @@ -18,10 +18,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "Core.h" #include "Shader.h" -#ifdef BBGE_BUILD_WINDOWS - #include -#endif #ifdef BBGE_BUILD_SHADERS // GL_ARB_shader_objects @@ -88,40 +86,16 @@ void Shader::setValue(float x, float y, float z, float w) unsigned char *readShaderFile( const char *fileName ) { debugLog("readShaderFile()"); -#ifdef BBGE_BUILD_WINDOWS - FILE *file = fopen( fileName, "r" ); - if( file == NULL ) - { - errorLog("Cannot open shader file!"); - return 0; - } + ttvfs::VFSFile *vf = core->vfs.GetFile(fileName); + if(!vf) + return NULL; - struct _stat fileStats; - - if( _stat( fileName, &fileStats ) != 0 ) - { - errorLog("Cannot get file stats for shader file!"); - return 0; - } - - - unsigned char *buffer = new unsigned char[fileStats.st_size]; - - int bytes = fread( buffer, 1, fileStats.st_size, file ); - - buffer[bytes] = 0; - - fclose( file ); - - debugLog("End readShaderFile()"); - - return buffer; - -#else - debugLog("End readShaderFile()"); - return 0; -#endif + vf->getBuf(); + unsigned char *buf = new unsigned char[vf->size() + 1]; + memcpy(buf, vf->getBuf(), vf->size() + 1); + core->addVFSFileForDrop(vf); + return buf; } void Shader::reload() diff --git a/BBGE/SimpleIStringStream.h b/BBGE/SimpleIStringStream.h index f323c04..a59f63c 100644 --- a/BBGE/SimpleIStringStream.h +++ b/BBGE/SimpleIStringStream.h @@ -93,7 +93,7 @@ class SimpleIStringStream { public: /* Reuse flag passed to StringStream(char *,int). */ - enum { + enum Mode { /* Make a copy of the buffer (default action). */ COPY, /* Use the passed-in string pointer as is. Requires the string @@ -212,7 +212,7 @@ class SimpleIStringStream { /*-------------------------------------------------------------------*/ - private: + protected: char *buffer; // The buffer we're parsing. char *position; // Our current position in the buffer. bool freeOnDestroy; // Should we free the buffer when we're destroyed? diff --git a/BBGE/SoundManager.cpp b/BBGE/SoundManager.cpp index e5a52f0..3ac9c21 100644 --- a/BBGE/SoundManager.cpp +++ b/BBGE/SoundManager.cpp @@ -21,7 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "SoundManager.h" #include "Core.h" #include "Base.h" -#include "PackRead.h" #if defined(BBGE_BUILD_FMODEX) #ifdef BBGE_BUILD_FMOD_OPENAL_BRIDGE @@ -134,20 +133,14 @@ FMOD_RESULT F_CALLBACK myopen(const char *name, int unicode, unsigned int *files { if (name) { - FILE *fp; - - fp = fopen(name, "rb"); - if (!fp) - { + ttvfs::VFSFile *vf = core->vfs.GetFile(name); + if(!vf) return FMOD_ERR_FILE_NOTFOUND; - } - - fseek(fp, 0, SEEK_END); - *filesize = ftell(fp); - fseek(fp, 0, SEEK_SET); + vf->open(); + *filesize = vf->size(); + *handle = (void*)vf; *userdata = (void *)0x12345678; - *handle = fp; } return FMOD_OK; @@ -160,7 +153,9 @@ FMOD_RESULT F_CALLBACK myclose(void *handle, void *userdata) return FMOD_ERR_INVALID_PARAM; } - fclose((FILE *)handle); + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)handle; + vf->close(); + core->addVFSFileForDrop(vf); return FMOD_OK; } @@ -174,7 +169,8 @@ FMOD_RESULT F_CALLBACK myread(void *handle, void *buffer, unsigned int sizebytes if (bytesread) { - *bytesread = (int)fread(buffer, 1, sizebytes, (FILE *)handle); + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)handle; + *bytesread = vf->read((char*)buffer, sizebytes); if (*bytesread < sizebytes) { @@ -192,7 +188,8 @@ FMOD_RESULT F_CALLBACK myseek(void *handle, unsigned int pos, void *userdata) return FMOD_ERR_INVALID_PARAM; } - fseek((FILE *)handle, pos, SEEK_SET); + ttvfs::VFSFile *vf = (ttvfs::VFSFile*)handle; + vf->seek(pos); return FMOD_OK; } @@ -332,7 +329,7 @@ SoundManager::SoundManager(const std::string &defaultDevice) debugLog("err_output_createbuffer, speaker mode"); result = SoundCore::system->setSpeakerMode(FMOD_SPEAKERMODE_STEREO); if (checkError()) goto get_out; - + debugLog("init 2"); result = SoundCore::system->init(channels, FMOD_INIT_NORMAL, 0, defaultDevice); /* Replace with whatever channel count and flags you use! */ if (checkError()) goto get_out; diff --git a/BBGE/TTFFont.cpp b/BBGE/TTFFont.cpp index 410af37..c032c5f 100644 --- a/BBGE/TTFFont.cpp +++ b/BBGE/TTFFont.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "Core.h" #include "TTFFont.h" @@ -46,8 +47,17 @@ void TTFFont::destroy() void TTFFont::load(const std::string &str, int sz) { - font = new FTGLTextureFont(str.c_str()); - font->FaceSize(sz); + ttvfs::VFSFile *vf = core->vfs.GetFile(str.c_str()); + if(!vf) + { + font = new FTGLTextureFont(str.c_str()); // file not in VFS, just pretend nothing happened + font->FaceSize(sz); + return; + } + + const unsigned char *buf = (const unsigned char*)vf->getBuf(); + create(buf, vf->size(), sz); // this copies the buffer internally + core->addVFSFileForDrop(vf); // so we can delete our own } void TTFFont::create(const unsigned char *data, unsigned long datalen, int sz) diff --git a/BBGE/Texture.cpp b/BBGE/Texture.cpp index e25b192..d8af0b4 100644 --- a/BBGE/Texture.cpp +++ b/BBGE/Texture.cpp @@ -468,6 +468,20 @@ void Texture::loadPNG(const std::string &file) { if (file.empty()) return; + ttvfs::VFSFile *vf = core->vfs.GetFile(file.c_str()); + const char *memptr = vf ? (const char*)vf->getBuf() : NULL; + if(!memptr) + { + debugLog("Can't load PNG file: " + file); + width = 64; + height = 64; + Texture::textureError = TEXERR_FILENOTFOUND; + //exit(1); + return; + } + + int memsize = vf->size(); + #ifdef BBGE_BUILD_OPENGL @@ -483,11 +497,11 @@ void Texture::loadPNG(const std::string &file) if (filter == GL_NEAREST) { - textures[0] = pngBind(file.c_str(), PNG_NOMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, filter, filter); + textures[0] = pngBindMem(memptr, memsize, PNG_NOMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, filter, filter); } else { - textures[0] = pngBind(file.c_str(), PNG_BUILDMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, GL_LINEAR_MIPMAP_LINEAR, filter); + textures[0] = pngBindMem(memptr, memsize, PNG_BUILDMIPMAPS, pngType, &info, GL_CLAMP_TO_EDGE, GL_LINEAR_MIPMAP_LINEAR, filter); } @@ -515,9 +529,9 @@ void Texture::loadPNG(const std::string &file) Texture::textureError = TEXERR_FILENOTFOUND; //exit(1); } - - #endif + + core->addVFSFileForDrop(vf); } // internal load functions diff --git a/BBGE/VFSFileStream.cpp b/BBGE/VFSFileStream.cpp new file mode 100644 index 0000000..6f820db --- /dev/null +++ b/BBGE/VFSFileStream.cpp @@ -0,0 +1,57 @@ +#include "Core.h" +#include "VFSFileStream.h" + +VFSTextStreamIn::VFSTextStreamIn(const std::string& fn, SimpleIStringStream::Mode strmode /* = TAKE_OVER*/) +: SimpleIStringStream() +{ + _init(fn.c_str(), strmode); +} + +VFSTextStreamIn::VFSTextStreamIn(const char *fn, SimpleIStringStream::Mode strmode /* = TAKE_OVER*/) +: SimpleIStringStream() +{ + _init(fn, strmode); +} + +void VFSTextStreamIn::_init(const char *fn, SimpleIStringStream::Mode strmode) +{ + ttvfs::VFSFile *vf = core->vfs.GetFile(fn); + if(vf) + { + vf->open(NULL, "r"); + setString((char*)vf->getBuf(), strmode); + vf->close(); + if(strmode == TAKE_OVER) + vf->dropBuf(false); + } + else + error = true; +} + + +VFSTextStdStreamIn::VFSTextStdStreamIn(const std::string& fn, SimpleIStringStream::Mode strmode /* = TAKE_OVER*/) +: std::istringstream() +{ + _init(fn.c_str(), strmode); +} + +VFSTextStdStreamIn::VFSTextStdStreamIn(const char *fn, SimpleIStringStream::Mode strmode /* = TAKE_OVER*/) +: std::istringstream() +{ + _init(fn, strmode); +} + +void VFSTextStdStreamIn::_init(const char *fn, SimpleIStringStream::Mode strmode) +{ + ttvfs::VFSFile *vf = core->vfs.GetFile(fn); + if(vf) + { + vf->open(NULL, "r"); + str((char*)vf->getBuf()); // stringstream will always make a copy + vf->close(); + if(strmode == SimpleIStringStream::TAKE_OVER) + core->addVFSFileForDrop(vf); + } + else + this->setstate(std::ios_base::failbit); +} diff --git a/BBGE/VFSFileStream.h b/BBGE/VFSFileStream.h new file mode 100644 index 0000000..16c7494 --- /dev/null +++ b/BBGE/VFSFileStream.h @@ -0,0 +1,43 @@ +#ifndef VFS_FILE_STREAM_H +#define VFS_FILE_STREAM_H + +#include "SimpleIStringStream.h" + +class VFSTextStreamIn : public SimpleIStringStream +{ + /* This class is an adapter to support STL-like read-only file streams for VFS files, + * using the SimpleIStringStream for performance reasons. + * + * strmode: one of COPY, REUSE, TAKE_OVER, see SimpleIStringStream.h + */ + +public: + VFSTextStreamIn(const char *fn, SimpleIStringStream::Mode strmode = TAKE_OVER); + VFSTextStreamIn(const std::string& fn, SimpleIStringStream::Mode strmode = TAKE_OVER); + void close() {} + +private: + void _init(const char *fn, SimpleIStringStream::Mode strmode); +}; + +class VFSTextStdStreamIn : public std::istringstream +{ + /* This class is an adapter to support STL-like read-only file streams for VFS files, + * using std::istringstream. + * + * strmode: one of COPY, REUSE, TAKE_OVER, see SimpleIStringStream.h + * - Note: The file's content will always be copied, regardless of strmode setting. + * However, TAKE_OVER will drop the internal buffer. + */ + +public: + VFSTextStdStreamIn(const char *fn, SimpleIStringStream::Mode strmode = SimpleIStringStream::TAKE_OVER); + VFSTextStdStreamIn(const std::string& fn, SimpleIStringStream::Mode strmode = SimpleIStringStream::TAKE_OVER); + void close() {} + +private: + void _init(const char *fn, SimpleIStringStream::Mode strmode); +}; + + +#endif diff --git a/BBGE/VFSIncludes.h b/BBGE/VFSIncludes.h new file mode 100644 index 0000000..4e44cf6 --- /dev/null +++ b/BBGE/VFSIncludes.h @@ -0,0 +1,9 @@ +#ifndef CUSTOM_VFS_INCLUDES_H +#define CUSTOM_VFS_INCLUDES_H + +#include "VFS.h" +#include "VFSFileStream.h" + +#include "VFSFile.h" + +#endif diff --git a/BBGE/glfont2.cpp b/BBGE/glfont2.cpp index 3f34acb..3cd1c18 100644 --- a/BBGE/glfont2.cpp +++ b/BBGE/glfont2.cpp @@ -20,33 +20,13 @@ using namespace std; #include */ #include "Base.h" - -#include "SDL_endian.h" +#include "Core.h" +#include "lvpa/ByteBuffer.h" //glFont header #include "glfont2.h" using namespace glfont; -static int read_int(ifstream &input) -{ - int buffer; - - input.read((char *)&buffer, 4); - return SDL_SwapLE32(buffer); -} - -static float read_float(ifstream &input) -{ - union - { - int i; - float f; - } buffer; - - input.read((char *)&buffer.i, 4); - buffer.i = SDL_SwapLE32(buffer.i); - return buffer.f; -} //******************************************************************* //GLFont Class Implementation @@ -70,7 +50,6 @@ GLFont::~GLFont () //******************************************************************* bool GLFont::Create (const char *file_name, int tex, bool loadTexture) { - ifstream input; int num_chars, num_tex_bytes; char *tex_bytes; @@ -78,19 +57,24 @@ bool GLFont::Create (const char *file_name, int tex, bool loadTexture) Destroy(); //Open input file - input.open(file_name, ios::in | ios::binary); - if (!input) - return false; + ttvfs::VFSFile *vf = core->vfs.GetFile(file_name); + if(!vf) + return false; + + lvpa::ByteBuffer bb; + bb.append(vf->getBuf(), vf->size()); + core->addVFSFileForDrop(vf); + lvpa::uint32 dummy; + + // Read the header from file + header.tex = tex; + bb >> dummy; // skip tex field + bb >> header.tex_width; + bb >> header.tex_height; + bb >> header.start_char; + bb >> header.end_char; + bb >> dummy; // skip chars field - // Read the header from file - header.tex = tex; - input.seekg(4, ios::cur); // skip tex field - header.tex_width = read_int(input); - header.tex_height = read_int(input); - header.start_char = read_int(input); - header.end_char = read_int(input); - input.seekg(4, ios::cur); // skip chars field - std::ostringstream os; os << "tex_width: " << header.tex_width << " tex_height: " << header.tex_height; debugLog(os.str()); @@ -103,18 +87,19 @@ bool GLFont::Create (const char *file_name, int tex, bool loadTexture) //Read character array for (int i = 0; i < num_chars; i++) { - header.chars[i].dx = read_float(input); - header.chars[i].dy = read_float(input); - header.chars[i].tx1 = read_float(input); - header.chars[i].ty1 = read_float(input); - header.chars[i].tx2 = read_float(input); - header.chars[i].ty2 = read_float(input); + bb >> header.chars[i].dx; + bb >> header.chars[i].dy; + bb >> header.chars[i].tx1; + bb >> header.chars[i].ty1; + bb >> header.chars[i].tx2; + bb >> header.chars[i].ty2; } //Read texture pixel data num_tex_bytes = header.tex_width * header.tex_height * 2; tex_bytes = new char[num_tex_bytes]; - input.read(tex_bytes, num_tex_bytes); + //input.read(tex_bytes, num_tex_bytes); + bb.read(tex_bytes, num_tex_bytes); //Build2DMipmaps(3, header.tex_width, header.tex_height, GL_UNSIGNED_BYTE, tex_bytes, 1); @@ -150,9 +135,6 @@ bool GLFont::Create (const char *file_name, int tex, bool loadTexture) //Free texture pixels memory delete[] tex_bytes; - //Close input file - input.close(); - //Return successfully return true; } diff --git a/BBGE/tinyxml.cpp b/BBGE/tinyxml.cpp index 9be6c6a..81f6d13 100644 --- a/BBGE/tinyxml.cpp +++ b/BBGE/tinyxml.cpp @@ -22,6 +22,8 @@ must not be misrepresented as being the original software. distribution. */ +// hacked VFS support into this version. + #include #ifdef TIXML_USE_STL @@ -31,6 +33,8 @@ distribution. #include "tinyxml.h" +#include "Core.h" + FILE* TiXmlFOpen( const char* filename, const char* mode ); bool TiXmlBase::condenseWhiteSpace = true; @@ -923,12 +927,11 @@ bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) value = filename; // reading in binary mode so that tinyxml can normalize the EOL - FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + ttvfs::VFSFile* file = core->vfs.GetFile(value.c_str()); if ( file ) { bool result = LoadFile( file, encoding ); - fclose( file ); return result; } else @@ -938,7 +941,7 @@ bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) } } -bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +bool TiXmlDocument::LoadFile( ttvfs::VFSFile* file, TiXmlEncoding encoding ) { if ( !file ) { @@ -951,10 +954,15 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) location.Clear(); // Get the file size, so we can pre-allocate the string. HUGE speed impact. - long length = 0; - fseek( file, 0, SEEK_END ); - length = ftell( file ); - fseek( file, 0, SEEK_SET ); + char* buf = (char*)file->getBuf(); + + if ( !buf ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + long length = file->size(); // Strange case, but good to handle up front. if ( length <= 0 ) @@ -984,15 +992,6 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) } */ - char* buf = new char[ length+1 ]; - buf[0] = 0; - - if ( fread( buf, length, 1, file ) != 1 ) { - delete [] buf; - SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); - return false; - } - // Process the buffer in place to normalize new lines. (See comment above.) // Copies from the 'p' to 'q' pointer, where p can advance faster if // a newline-carriage return is hit. @@ -1031,13 +1030,16 @@ bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) Parse( buf, 0, encoding ); - delete [] buf; + core->addVFSFileForDrop(file); + return !Error(); } bool TiXmlDocument::SaveFile( const char * filename ) const { + // FG: TODO: use VFS stuff here as well + // The old c stuff lives on... FILE* fp = TiXmlFOpen( filename, "w" ); if ( fp ) diff --git a/BBGE/tinyxml.h b/BBGE/tinyxml.h index 0182291..9491fc2 100644 --- a/BBGE/tinyxml.h +++ b/BBGE/tinyxml.h @@ -53,6 +53,11 @@ distribution. #define TIXML_STRING TiXmlString #endif +namespace ttvfs +{ + class VFSFile; +} + // Deprecated library function hell. Compilers want to use the // new safe versions. This probably doesn't fully address the problem, // but it gets closer. There are too many compilers for me to fully @@ -1417,7 +1422,7 @@ public: will be interpreted as an XML file. TinyXML doesn't stream in XML from the current file location. Streaming may be added in the future. */ - bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + bool LoadFile( ttvfs::VFSFile*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the given FILE*. Returns true if successful. bool SaveFile( FILE* ) const; diff --git a/CMakeLists.txt b/CMakeLists.txt index 9969e93..77f0be5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,8 @@ INCLUDE_DIRECTORIES(${LUA_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${OGGVORBIS_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${EXTLIBDIR}/ttvfs) +INCLUDE_DIRECTORIES(${EXTLIBDIR}/lvpa) # Custom build ID: e.g. "-custom", " (my very own build)" @@ -378,6 +380,7 @@ SET(BBGE_SRCS ${BBGEDIR}/tinyxmlerror.cpp ${BBGEDIR}/tinyxmlparser.cpp ${BBGEDIR}/glfont2.cpp + ${BBGEDIR}/VFSFileStream.cpp ${COCOA_SRCS} ${EXTLIBDIR}/glpng/glpng.c ${EXTLIBDIR}/glpng/png/png.c @@ -557,6 +560,9 @@ SET(LUA_SRCS ${LUASRCDIR}/lmathlib.c ) +ADD_SUBDIRECTORY(ExternalLibs/ttvfs) + +SET(OPTIONAL_LIBS ${OPTIONAL_LIBS} ttvfs) IF(MACOSX) SET(OPTIONAL_LIBS ${OPTIONAL_LIBS} "-framework Carbon") diff --git a/ExternalLibs/glpng.h b/ExternalLibs/glpng.h index 8c9aa2f..96188a1 100644 --- a/ExternalLibs/glpng.h +++ b/ExternalLibs/glpng.h @@ -97,9 +97,11 @@ extern int APIENTRY pngLoadRawF(FILE *file, pngRawInfo *rawinfo); extern int APIENTRY pngLoad(const char *filename, int mipmap, int trans, pngInfo *info); extern int APIENTRY pngLoadF(FILE *file, int mipmap, int trans, pngInfo *info); +extern int APIENTRY pngLoadMem(const char *mem, int size, int mipmap, int trans, pngInfo *info); extern unsigned int APIENTRY pngBind(const char *filename, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter); extern unsigned int APIENTRY pngBindF(FILE *file, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter); +extern unsigned int APIENTRY pngBindMem(const char *mem, int size, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter); extern void APIENTRY pngSetStencil(unsigned char red, unsigned char green, unsigned char blue); extern void APIENTRY pngSetAlphaCallback(unsigned char (*callback)(unsigned char red, unsigned char green, unsigned char blue)); diff --git a/ExternalLibs/glpng/glpng.c b/ExternalLibs/glpng/glpng.c index e1da8aa..3203537 100644 --- a/ExternalLibs/glpng/glpng.c +++ b/ExternalLibs/glpng/glpng.c @@ -691,6 +691,14 @@ unsigned int APIENTRY pngBindF(FILE *file, int mipmap, int trans, pngInfo *info, return 0; } +unsigned int APIENTRY pngBindMem(const char *mem, int size, int mipmap, int trans, pngInfo *info, int wrapst, int minfilter, int magfilter) { + unsigned int id = SetParams(wrapst, magfilter, minfilter); + + if (id != 0 && pngLoadMem(mem, size, mipmap, trans, info)) + return id; + return 0; +} + void APIENTRY pngSetStencil(unsigned char red, unsigned char green, unsigned char blue) { StencilRed = red, StencilGreen = green, StencilBlue = blue; } @@ -717,3 +725,332 @@ void APIENTRY pngSetStandardOrientation(int standardorientation) { StandardOrientation = standardorientation; } + + +// -- added memory read functions -- + +/*pointer to a new input function that takes as its +arguments a pointer to a png_struct, a pointer to +a location where input data can be stored, and a 32-bit +unsigned int that is the number of bytes to be read. +To exit and output any fatal error messages the new write +function should call png_error(png_ptr, "Error msg"). */ + +typedef struct glpng_memread_struct +{ + png_bytep mem; + png_size_t rpos; +} glpng_memread; + +void glpng_read_mem(png_structp png, png_bytep dst, png_size_t size) +{ + glpng_memread *mr = (glpng_memread*)png->io_ptr; + memcpy(dst, mr->mem + mr->rpos, size); + mr->rpos += size; +} + +int APIENTRY pngLoadMem(const char *mem, int size, int mipmap, int trans, pngInfo *pinfo) { + GLint pack, unpack; + unsigned char header[8]; + png_structp png; + png_infop info; + png_infop endinfo; + png_bytep data, data2; + png_bytep *row_p; + double fileGamma; + + png_uint_32 width, height, rw, rh; + int depth, color; + + png_uint_32 i; + glpng_memread memread; + + if(size < 8) + return 0; // error + + memcpy(header, mem, 8); + + if (!png_check_sig(header, 8)) + return 0; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + info = png_create_info_struct(png); + endinfo = png_create_info_struct(png); + + // DH: added following lines + if (setjmp(png->jmpbuf)) + { + png_destroy_read_struct(&png, &info, &endinfo); + return 0; + } + // ~DH + + memread.rpos = 0; + memread.mem = ((png_bytep)mem) + 8; + png_set_read_fn(png, (voidp)&memread, glpng_read_mem); + png_set_sig_bytes(png, 8); + png_read_info(png, info); + png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL); + + if (pinfo != NULL) { + pinfo->Width = width; + pinfo->Height = height; + pinfo->Depth = depth; + } + + if (MaxTextureSize == 0) + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxTextureSize); + +#ifdef SUPPORTS_PALETTE_EXT +#ifdef _WIN32 + if (PalettedTextures == -1) + PalettedTextures = ExtSupported("GL_EXT_paletted_texture") && (strstr((const char *) glGetString(GL_VERSION), "1.1.0 3Dfx Beta") == NULL); + + if (PalettedTextures) { + if (glColorTableEXT == NULL) { + glColorTableEXT = (PFNGLCOLORTABLEEXTPROC) wglGetProcAddress("glColorTableEXT"); + if (glColorTableEXT == NULL) + PalettedTextures = 0; + } + } +#endif +#endif + + if (PalettedTextures == -1) + PalettedTextures = 0; + + if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + if (color&PNG_COLOR_MASK_ALPHA && trans != PNG_ALPHA) { + png_set_strip_alpha(png); + color &= ~PNG_COLOR_MASK_ALPHA; + } + + if (!(PalettedTextures && mipmap >= 0 && trans == PNG_SOLID)) + if (color == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png); + + /*--GAMMA--*/ + checkForGammaEnv(); + if (png_get_gAMA(png, info, &fileGamma)) + png_set_gamma(png, screenGamma, fileGamma); + else + png_set_gamma(png, screenGamma, 1.0/2.2); + + png_read_update_info(png, info); + + data = (png_bytep) malloc(png_get_rowbytes(png, info)*height); + row_p = (png_bytep *) malloc(sizeof(png_bytep)*height); + + for (i = 0; i < height; i++) { + if (StandardOrientation) + row_p[height - 1 - i] = &data[png_get_rowbytes(png, info)*i]; + else + row_p[i] = &data[png_get_rowbytes(png, info)*i]; + } + + png_read_image(png, row_p); + free(row_p); + + rw = SafeSize(width), rh = SafeSize(height); + + if (rw != width || rh != height) { + const int channels = png_get_rowbytes(png, info)/width; + + data2 = (png_bytep) malloc(rw*rh*channels); + + /* Doesn't work on certain sizes */ + /* if (gluScaleImage(glformat, width, height, GL_UNSIGNED_BYTE, data, rw, rh, GL_UNSIGNED_BYTE, data2) != 0) + return 0; + */ + Resize(channels, data, width, height, data2, rw, rh); + + width = rw, height = rh; + free(data); + data = data2; + } + + { /* OpenGL stuff */ + glGetIntegerv(GL_PACK_ALIGNMENT, &pack); + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + +#ifdef SUPPORTS_PALETTE_EXT + if (PalettedTextures && mipmap >= 0 && trans == PNG_SOLID && color == PNG_COLOR_TYPE_PALETTE) { + png_colorp pal; + int cols; + GLint intf; + + if (pinfo != NULL) pinfo->Alpha = 0; + png_get_PLTE(png, info, &pal, &cols); + + switch (cols) { + case 1<<1: intf = GL_COLOR_INDEX1_EXT; break; + case 1<<2: intf = GL_COLOR_INDEX2_EXT; break; + case 1<<4: intf = GL_COLOR_INDEX4_EXT; break; + case 1<<8: intf = GL_COLOR_INDEX8_EXT; break; + case 1<<12: intf = GL_COLOR_INDEX12_EXT; break; + case 1<<16: intf = GL_COLOR_INDEX16_EXT; break; + default: + /*printf("Warning: Colour depth %i not recognised\n", cols);*/ + return 0; + } + glColorTableEXT(GL_TEXTURE_2D, GL_RGB8, cols, GL_RGB, GL_UNSIGNED_BYTE, pal); + glTexImage2D(GL_TEXTURE_2D, mipmap, intf, width, height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data); + } + else +#endif + if (trans == PNG_SOLID || trans == PNG_ALPHA || trans == PNG_LUMINANCEALPHA || color == PNG_COLOR_TYPE_RGB_ALPHA || color == PNG_COLOR_TYPE_GRAY_ALPHA) { + GLenum glformat; + GLint glcomponent; + + switch (color) { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_PALETTE: + glformat = GL_RGB; + glcomponent = 3; + if (pinfo != NULL) pinfo->Alpha = 0; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB_ALPHA: + glformat = GL_RGBA; + glcomponent = 4; + if (pinfo != NULL) pinfo->Alpha = 8; + break; + + default: + /*puts("glformat not set");*/ + return 0; + } + + if (trans == PNG_LUMINANCEALPHA) + glformat = GL_LUMINANCE_ALPHA; + + if (mipmap == PNG_BUILDMIPMAPS) + Build2DMipmaps(glcomponent, width, height, glformat, data, 1); + else if (mipmap == PNG_SIMPLEMIPMAPS) + Build2DMipmaps(glcomponent, width, height, glformat, data, 0); + else + glTexImage2D(GL_TEXTURE_2D, mipmap, glcomponent, width, height, 0, glformat, GL_UNSIGNED_BYTE, data); + } + else { + png_bytep p, endp, q; + int r, g, b, a; + + p = data, endp = p+width*height*3; + q = data2 = (png_bytep) malloc(sizeof(png_byte)*width*height*4); + + if (pinfo != NULL) pinfo->Alpha = 8; + +#define FORSTART \ + do { \ + r = *p++; /*red */ \ + g = *p++; /*green*/ \ + b = *p++; /*blue */ \ + *q++ = r; \ + *q++ = g; \ + *q++ = b; + +#define FOREND \ + q++; \ + } while (p != endp); + +#define ALPHA *q + + switch (trans) { + case PNG_CALLBACK: + FORSTART + ALPHA = AlphaCallback((unsigned char) r, (unsigned char) g, (unsigned char) b); + FOREND + break; + + case PNG_STENCIL: + FORSTART + if (r == StencilRed && g == StencilGreen && b == StencilBlue) + ALPHA = 0; + else + ALPHA = 255; + FOREND + break; + + case PNG_BLEND1: + FORSTART + a = r+g+b; + if (a > 255) ALPHA = 255; else ALPHA = a; + FOREND + break; + + case PNG_BLEND2: + FORSTART + a = r+g+b; + if (a > 255*2) ALPHA = 255; else ALPHA = a/2; + FOREND + break; + + case PNG_BLEND3: + FORSTART + ALPHA = (r+g+b)/3; + FOREND + break; + + case PNG_BLEND4: + FORSTART + a = r*r+g*g+b*b; + if (a > 255) ALPHA = 255; else ALPHA = a; + FOREND + break; + + case PNG_BLEND5: + FORSTART + a = r*r+g*g+b*b; + if (a > 255*2) ALPHA = 255; else ALPHA = a/2; + FOREND + break; + + case PNG_BLEND6: + FORSTART + a = r*r+g*g+b*b; + if (a > 255*3) ALPHA = 255; else ALPHA = a/3; + FOREND + break; + + //HACK: disabling this for now + /* + case PNG_BLEND7: + FORSTART + a = r*r+g*g+b*b; + if (a > 255*255) ALPHA = 255; else ALPHA = (int) (sqrt(float(a))); + FOREND + */ + break; + } + +#undef FORSTART +#undef FOREND +#undef ALPHA + + if (mipmap == PNG_BUILDMIPMAPS) + Build2DMipmaps(4, width, height, GL_RGBA, data2, 1); + else if (mipmap == PNG_SIMPLEMIPMAPS) + Build2DMipmaps(4, width, height, GL_RGBA, data2, 0); + else + glTexImage2D(GL_TEXTURE_2D, mipmap, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2); + + free(data2); + } + + glPixelStorei(GL_PACK_ALIGNMENT, pack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + } /* OpenGL end */ + + png_read_end(png, endinfo); + png_destroy_read_struct(&png, &info, &endinfo); + + free(data); + + return 1; +} diff --git a/ExternalLibs/lvpa/ByteBuffer.h b/ExternalLibs/lvpa/ByteBuffer.h new file mode 100644 index 0000000..bcf3082 --- /dev/null +++ b/ExternalLibs/lvpa/ByteBuffer.h @@ -0,0 +1,340 @@ +#ifndef BYTEBUFFER_H +#define BYTEBUFFER_H + +#include "LVPACommon.h" +#include "ByteConverter.h" + +#include // for memcpy + + +LVPA_NAMESPACE_START + + +#define BB_MAKE_WRITE_OP(T) inline ByteBuffer& operator<<(T val) { append(val); return *this; } +#define BB_MAKE_READ_OP(T) inline ByteBuffer& operator>>(T &val) { val = read(); return *this; } + +class ByteBuffer +{ +public: + typedef void (*delete_func)(void*); + enum Mode // for creation with existing pointers + { + COPY, //- Make a copy of the buffer (default action). + REUSE, //- Use the passed-in buffer as is. Requires the pointer + // to remain valid over the life of this object. + TAKE_OVER, //- Take over the passed-in buffer; it will be deleted on object destruction. + }; + + class Exception + { + public: + Exception(const ByteBuffer *bb, const char *act, uint32 sp = 0) + { + action = act; + rpos = bb->rpos(); + wpos = bb->wpos(); + sizeparam = sp; + cursize = bb->size(); + } + uint32 rpos, wpos, sizeparam, cursize; + const char *action; + }; + +private: + + delete_func _delfunc; + uint32 _rpos, // read position, [0 ... _size] + _wpos, // write position, [0 ... _size] + _res, // reserved buffer size, [0 ... _size ... _res] + _size; // used buffer size + + uint8 *_buf; // the ptr to the buffer that holds all the bytes + bool _mybuf; // if true, destructor deletes buffer + bool _growable; // default true, if false, buffer will not re-allocate more space + +public: + + + ByteBuffer() + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true) + { + _allocate(128); + } + ByteBuffer(uint32 res) + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true) + { + _allocate(res); + } + ByteBuffer(const ByteBuffer &buf, uint32 extra = 0) + : _rpos(0), _wpos(0), _buf(NULL), _size(0), _growable(true) + { + _allocate(buf.size() + extra + 64); + append(buf); + } + ByteBuffer(void *buf, uint32 size, Mode mode = COPY, delete_func del = NULL, uint32 extra = 0) + : _rpos(0), _wpos(0), _size(size), _buf(NULL), _growable(true), _delfunc(del), + _mybuf(false) // for mode == REUSE + { + switch(mode) + { + case COPY: + _allocate(size + extra); + append(buf, size); + break; + + case TAKE_OVER: + _mybuf = true; // fallthrough + case REUSE: + _buf = (uint8*)buf; + _res = size; + } + } + + virtual ~ByteBuffer() + { + clear(); + } + + void clear(void) + { + _delete(); + reset(); + } + + inline void reset(void) + { + _rpos = _wpos = _size = 0; + } + + void resize(uint32 newsize) + { + reserve(newsize); + _rpos = 0; + _wpos = newsize; + _size = newsize; + } + + void reserve(uint32 newsize) + { + if(_res < newsize) + _allocate(newsize); + } + + // ---------------------- Write methods ----------------------- + + BB_MAKE_WRITE_OP(uint8); + BB_MAKE_WRITE_OP(uint16); + BB_MAKE_WRITE_OP(uint32); + BB_MAKE_WRITE_OP(uint64); + BB_MAKE_WRITE_OP(float); + BB_MAKE_WRITE_OP(double); + BB_MAKE_WRITE_OP(int); + + ByteBuffer &operator<<(bool value) + { + append((char)value); + return *this; + } + + ByteBuffer &operator<<(const char *str) + { + append((uint8 *)str, str ? strlen(str) : 0); + append((uint8)0); + return *this; + } + + ByteBuffer &operator<<(const std::string &value) + { + append((uint8 *)value.c_str(), value.length()); + append((uint8)0); + return *this; + } + + // -------------------- Read methods -------------------- + + BB_MAKE_READ_OP(uint8); + BB_MAKE_READ_OP(uint16); + BB_MAKE_READ_OP(uint32); + BB_MAKE_READ_OP(uint64); + BB_MAKE_READ_OP(float); + BB_MAKE_READ_OP(double); + BB_MAKE_READ_OP(int); + + ByteBuffer &operator>>(bool &value) + { + value = read() > 0 ? true : false; + return *this; + } + + uint8 operator[](uint32 pos) + { + return read(pos); + } + + ByteBuffer &operator>>(std::string& value) + { + value.clear(); + char c; + while(readable() && (c = read())) + value += c; + return *this; + } + + // -------------------------------------------------- + + uint32 rpos() const { return _rpos; } + uint32 rpos(uint32 rpos) + { + _rpos = rpos < size() ? rpos : size(); + return _rpos; + } + + uint32 wpos() const { return _wpos; } + uint32 wpos(uint32 wpos) + { + _wpos = wpos < size() ? wpos : size(); + return _wpos; + } + + template T read() + { + T r = read(_rpos); + _rpos += sizeof(T); + return r; + } + template T read(uint32 pos) const + { + if(pos + sizeof(T) > size()) + throw Exception(this, "read", sizeof(T)); + T val = *((T const*)(_buf + pos)); + ToLittleEndian(val); + return val; + } + + void read(void *dest, uint32 len) + { + if (_rpos + len <= size()) + memcpy(dest, &_buf[_rpos], len); + else + throw Exception(this, "read-into", len); + _rpos += len; + } + + inline const uint8 *contents() const { return _buf; } + inline uint8 *contents() { return _buf; } + + inline uint32 size() const { return _size; } + + inline uint32 bytes() const { return size(); } + inline uint32 bits() const { return bytes() * 8; } + + inline uint32 capacity() const { return _res; } + + inline uint32 readable(void) const { return size() - rpos(); } + inline uint32 writable(void) const { return size() - wpos(); } // free space left before realloc will occur + + template void append(T value) + { + ToLittleEndian(value); + _enlargeIfReq(_wpos + sizeof(T)); + *((T*)(_buf + _wpos)) = value; + _wpos += sizeof(T); + if(_size < _wpos) + _size = _wpos; + } + + void append(const void *src, uint32 bytes) + { + if (!bytes) return; + _enlargeIfReq(_wpos + bytes); + memcpy(_buf + _wpos, src, bytes); + _wpos += bytes; + if(_size < _wpos) + _size = _wpos; + } + void append(const ByteBuffer& buffer) + { + if(buffer.size()) + append(buffer.contents(), buffer.size()); + } + + void put(uint32 pos, const void *src, uint32 bytes) + { + memcpy(_buf + pos, src, bytes); + } + + template void put(uint32 pos, T value) + { + if(pos >= size()) + { + throw Exception(this, "put", sizeof(T)); + } + ToLittleEndian(value); + *((T*)(_buf + pos)) = value; + } + + inline bool growable(void) { return _growable; } + inline void growable(bool b) { _growable = b; } + + // dangerous functions + + void _setPtr(void *p) + { + _buf = (uint8*)p; + } + +protected: + + void _delete(void) + { + if(_mybuf) + { + if(_delfunc) + _delfunc(_buf); + else + delete [] _buf; + _buf = NULL; + _res = 0; + } + } + + // allocate larger buffer and copy contents. if we own the current buffer, delete old, otherwise, leave it as it is. + void _allocate(uint32 s) + { + if(!_growable && _buf) // only throw if we already have a buf + throw Exception(this, "_alloc+locked", s); + + uint8 *newbuf = (uint8*)malloc(s); + if(_buf) + { + memcpy(newbuf, _buf, _size); + _delete(); + } + _delfunc = free; + _buf = newbuf; + _res = s; + _mybuf = true; + } + + void _enlargeIfReq(uint32 minSize) + { + if(_res < minSize) + { + uint32 a = _res * 2; + if(a < minSize) // fallback if doubling the space was not enough + a += minSize; + _allocate(a); + } + } + +}; + + +#undef BB_MAKE_WRITE_OP +#undef BB_MAKE_READ_OP + + +LVPA_NAMESPACE_END + + +#endif diff --git a/ExternalLibs/lvpa/ByteConverter.h b/ExternalLibs/lvpa/ByteConverter.h new file mode 100644 index 0000000..74c40a7 --- /dev/null +++ b/ExternalLibs/lvpa/ByteConverter.h @@ -0,0 +1,46 @@ +#ifndef BYTECONVERTER_H +#define BYTECONVERTER_H + +#include +#include "LVPAInternal.h" // this is important to fix up any possible ***_ENDIAN misconfigurations + +LVPA_NAMESPACE_START + +namespace ByteConverter +{ + template + inline void convert(char *val) + { + std::swap(*val, *(val + T - 1)); + convert(val + 1); + } + + template<> inline void convert<0>(char *) {} + template<> inline void convert<1>(char *) {} + + template + inline void apply(T *val) + { + convert((char *)(val)); + } +} + +#if IS_BIG_ENDIAN +template inline void ToLittleEndian(T& val) { ByteConverter::apply(&val); } +template inline void ToBigEndian(T&) { } +#else +template inline void ToLittleEndian(T&) { } +template inline void ToBigEndian(T& val) { ByteConverter::apply(&val); } +#endif + +template void ToLittleEndian(T*); // will generate link error +template void ToBigEndian(T*); // will generate link error + +inline void ToLittleEndian(uint8&) { } +inline void ToLittleEndian(int8&) { } +inline void ToBigEndian(uint8&) { } +inline void ToBigEndian( int8&) { } + +LVPA_NAMESPACE_END + +#endif diff --git a/ExternalLibs/lvpa/LVPACommon.h b/ExternalLibs/lvpa/LVPACommon.h new file mode 100644 index 0000000..d5b9dcd --- /dev/null +++ b/ExternalLibs/lvpa/LVPACommon.h @@ -0,0 +1,44 @@ +#ifndef LVPA_COMMON_H +#define LVPA_COMMON_H + +#include "LVPACompileConfig.h" + +#include +#include + +LVPA_NAMESPACE_START + +#ifdef _MSC_VER + typedef __int64 int64; + typedef long int32; + typedef short int16; + typedef char int8; + typedef unsigned __int64 uint64; + typedef unsigned long uint32; + typedef unsigned short uint16; + typedef unsigned char uint8; +#else + typedef long long int64; + typedef int int32; + typedef short int16; + typedef char int8; + typedef unsigned long long uint64; + typedef unsigned int uint32; + typedef unsigned short uint16; + typedef unsigned char uint8; +#endif + +struct memblock +{ + memblock() : ptr(NULL), size(0) {} + memblock(uint8 *p, uint32 s) : size(s), ptr(p) {} + uint8 *ptr; + uint32 size; +}; + + +LVPA_NAMESPACE_END + + +#endif + diff --git a/ExternalLibs/lvpa/LVPACompileConfig.h b/ExternalLibs/lvpa/LVPACompileConfig.h new file mode 100644 index 0000000..c986aa4 --- /dev/null +++ b/ExternalLibs/lvpa/LVPACompileConfig.h @@ -0,0 +1,28 @@ +#ifndef LVPA_COMPILE_CONFIG +#define LVPA_COMPILE_CONFIG + +// TODO ADD TEXT +#define LVPA_NAMESPACE lvpa + +//#define LVPA_SUPPORT_ZLIB +#define LVPA_SUPPORT_LZMA +//#define LVPA_SUPPORT_LZO +#define LVPA_SUPPORT_LZF + + + +// ------ End of config ------ + +#ifdef LVPA_NAMESPACE +# define LVPA_NAMESPACE_START namespace LVPA_NAMESPACE { +# define LVPA_NAMESPACE_END } +# define LVPA_NAMESPACE_IMPL LVPA_NAMESPACE:: + namespace LVPA_NAMESPACE {} // predeclare namespace to make compilers happy +#else +# define LVPA_NAMESPACE_START +# define LVPA_NAMESPACE_END +# define LVPA_NAMESPACE_IMPL +#endif + + +#endif diff --git a/ExternalLibs/lvpa/LVPAInternal.h b/ExternalLibs/lvpa/LVPAInternal.h new file mode 100644 index 0000000..ea796a3 --- /dev/null +++ b/ExternalLibs/lvpa/LVPAInternal.h @@ -0,0 +1,201 @@ +#ifndef LVPA_INTERNAL_H +#define LVPA_INTERNAL_H + + +#ifdef _DEBUG +# define DBG if(1) +# define DEBUG(x) x; +# define logdebug(...) { printf(__VA_ARGS__); putchar('\n'); } +# define logerror(...) { fputs("ERROR: ",stdout); printf(__VA_ARGS__); putchar('\n'); } +#else +# define DBG if(0) +# define DEBUG(x) +# define logdebug(...) +# define logerror(...) +#endif + + +////////////////////////////////////// +// Platform defines +////////////////////////////////////// + +#define PLATFORM_WIN32 0 +#define PLATFORM_UNIX 1 +#define PLATFORM_APPLE 2 +#define PLATFORM_INTEL 3 + +#if defined( __WIN32__ ) || defined( WIN32 ) || defined( _WIN32 ) +# define PLATFORM PLATFORM_WIN32 +#elif defined( __APPLE_CC__ ) +# define PLATFORM PLATFORM_APPLE +#elif defined( __INTEL_COMPILER ) +# define PLATFORM PLATFORM_INTEL +#else +# define PLATFORM PLATFORM_UNIX +#endif + +#define COMPILER_MICROSOFT 0 +#define COMPILER_GNU 1 +#define COMPILER_BORLAND 2 +#define COMPILER_INTEL 3 + +#ifdef _MSC_VER +# define COMPILER COMPILER_MICROSOFT +#elif defined( __BORLANDC__ ) +# define COMPILER COMPILER_BORLAND +#elif defined( __INTEL_COMPILER ) +# define COMPILER COMPILER_INTEL +#elif defined( __GNUC__ ) +# define COMPILER COMPILER_GNU +#else +# pragma error "FATAL ERROR: Unknown compiler." +#endif + +// stupid warnings +#if COMPILER == COMPILER_MICROSOFT +# define _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_DEPRECATE +# pragma warning(disable: 4996) +#endif + +//////////////////////////////////// +// Compiler defines +//////////////////////////////////// + +#if COMPILER == COMPILER_MICROSOFT + #define I64FMT "%016I64X" + #define I64FMTD "%I64u" + #define I64LIT(x) (x ## i64) + #define UI64LIT(x) (x ## ui64) + #define snprintf _snprintf +#else + #define stricmp strcasecmp + #define strnicmp strncasecmp + #define I64FMT "%016llX" + #define I64FMTD "%llu" + #define I64LIT(x) (x ## LL) + #define UI64LIT(x) (x ## ULL) +#endif + +#ifndef _LP64 +# if defined (_M_IA64) || defined (__ia64__) || defined (_M_AMD64) || defined (__amd64) || defined(_M_X64) +# define _LP64 1 +# endif +#endif + +#ifdef _LP64 // to be set for 64 bit compile +# define PTRFMT "0x"I64FMT +# define SYSTEM_BITS 64 +#else +# define PTRFMT "0x%X" +# define SYSTEM_BITS 32 +#endif + +#ifndef SIGQUIT +#define SIGQUIT 3 +#endif + +#if COMPILER == COMPILER_MICROSOFT +# if _MSC_VER >= 1600 +# define COMPILER_NAME "VC100+" +# elif _MSC_VER >= 1500 +# define COMPILER_NAME "VC90" +# elif _MSC_VER >= 1400 +# define COMPILER_NAME "VC80" +# elif _MSC_VER >= 1310 +# define COMPILER_NAME "VC71" +# endif +# define COMPILER_VERSION _MSC_VER +# define COMPILER_VERSION_OUT "%u" +#elif COMPILER == COMPILER_GNU +# define COMPILER_NAME "GCC" +# ifndef __GNUC_PATCHLEVEL__ +# define __GNUC_PATCHLEVEL__ 0 +# endif +# define COMPILER_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# define COMPILER_VERSION_OUT "%u" +// TODO: add more compilers here when necessary +#else +# define COMPILER_NAME "unknown" +# define COMPILER_VERSION "unk" +# define COMPILER_VERSION_OUT "%s" +#endif + +#if PLATFORM == PLATFORM_UNIX +# define PLATFORM_NAME "Unix" +#elif PLATFORM == PLATFORM_WIN32 +# define PLATFORM_NAME "Win32" +#elif PLATFORM == PLATFORM_APPLE +# define PLATFORM_NAME "Apple" +// TODO: add more platforms here when necessary +#else +# define PLATFORM_NAME "unknown" +#endif + +#if COMPILER == COMPILER_GNU +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTF(F,V) __attribute__ ((format (printf, F, V))) +#else //COMPILER != COMPILER_GNU +# define ATTR_NORETURN +# define ATTR_PRINTF(F,V) +#endif //COMPILER == COMPILER_GNU + + +// taken from ACE +// have seen on some systems that both defines exist, so if that is is the case, rely on this detection here +#if (!defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)) || (defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)) +# if defined (i386) || defined (__i386__) || defined (_M_IX86) || \ + defined (vax) || defined (__alpha) || defined (__LITTLE_ENDIAN__) || \ + defined (ARM) || defined (_M_IA64) || defined (__ia64__) || \ + defined (_M_AMD64) || defined (__amd64) + // We know these are little endian. +# undef LITTLE_ENDIAN +# undef BIG_ENDIAN +# define LITTLE_ENDIAN 1 +# define IS_LITTLE_ENDIAN 1 +# define IS_BIG_ENDIAN 0 +# else + // Otherwise, we assume big endian. +# undef LITTLE_ENDIAN +# undef BIG_ENDIAN +# define BIG_ENDIAN 1 +# define IS_LITTLE_ENDIAN 0 +# define IS_BIG_ENDIAN 1 +# endif +#endif + +#include +#include +#include +#include +#include + +#define ASSERT(what) { if (!(what)) { fprintf( stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n", __FILE__, __LINE__,__FUNCTION__, #what); assert( #what &&0 ); } } + + + +#include "LVPACommon.h" + +LVPA_NAMESPACE_START + + +template class AutoPtrVector +{ +public: + inline AutoPtrVector(uint32 prealloc) :v(prealloc) + { + for(uint32 i = 0; i < prealloc; ++i) + v[i] = NULL; + } + inline ~AutoPtrVector() + { + for(uint32 i = 0; i < v.size(); ++i) + if(v[i]) + delete v[i]; + } + std::vector v; +}; + +LVPA_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/CMakeLists.txt b/ExternalLibs/ttvfs/CMakeLists.txt new file mode 100644 index 0000000..6e621bf --- /dev/null +++ b/ExternalLibs/ttvfs/CMakeLists.txt @@ -0,0 +1,64 @@ + +option(TTVFS_LARGEFILE_SUPPORT "Enable support for files > 4 GB? (experimental!)" FALSE) +option(TTVFS_IGNORE_CASE "Enable full case-insensitivity even on case-sensitive OSes like Linux and alike?" FALSE) + +# Be sure to copy this part to your root CMakeLists.txt if you prefer to use CMake for configuring +# instead of editing the headers directly! +# If you edit the headers, this is not necessary. +if(TTVFS_LARGEFILE_SUPPORT) + add_definitions("-DVFS_LARGEFILE_SUPPORT") +endif() +if(TTVFS_IGNORE_CASE) + add_definitions("-DVFS_IGNORE_CASE") +endif() +# --snip-- + + +# compiler specific things +if(MSVC) + # MSVC builds require installed runtime library by default + option(TTVFS_STATIC_LIB "Link as static library without runtime dependencies (Note: To get rid of this setting with MSVC, the cmake cache must be cleared)" FALSE) + add_definitions("/GR-") # run-time type info (RTTI) not required + + if(TTVFS_STATIC_LIB) + # this is ugly - hackfix compiler flags + foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + if(${flag_var} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MDd") + endforeach() + + # hackfix linker flags - no idea why, but MSVC will produce linker errors otherwise + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /NODEFAULTLIB") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:msvcrt.lib,msvcrtd.lib") # not sure if this is correct + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:msvcrt.lib,msvcrtd.lib") + endif() +endif() + + +set(ttvfs_SRC + VFS.h + VFSAtomic.cpp + VFSAtomic.h + VFSDefines.h + VFSDir.cpp + VFSDir.h + VFSFile.cpp + VFSFile.h + VFSHelper.cpp + VFSHelper.h + VFSInternal.h + VFSLoader.cpp + VFSLoader.h + VFSSelfRefCounter.h + VFSTools.cpp + VFSTools.h +) + +set(TTVFS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "ttvfs include directory - for external includers" FORCE) +set(TTVFS_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "ttvfs source directory - for external includers" FORCE) + +add_library(ttvfs ${ttvfs_SRC}) diff --git a/ExternalLibs/ttvfs/VFS.h b/ExternalLibs/ttvfs/VFS.h new file mode 100644 index 0000000..ad205f6 --- /dev/null +++ b/ExternalLibs/ttvfs/VFS.h @@ -0,0 +1,78 @@ +/* ttvfs -- tiny tree virtual file system + +// VFS.h - all the necessary includes to get a basic VFS working +// Only include externally, not inside the library. + +See VFSDefines.h for compile configration. + + +---------[ License ]---------- +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef TTVFS_VFS_H +#define TTVFS_VFS_H + +#include "VFSDefines.h" + +VFS_NAMESPACE_START +bool _checkCompatInternal(bool large, bool nocase, unsigned int vfspos_size); + +/** It is recommended to call this function early in your code + and ensure it returns true - if it does not, compiler settings + are inconsistent, which may cause otherwise hard to detect problems. */ +inline static bool checkCompat(void) +{ +#ifdef VFS_LARGEFILE_SUPPORT + bool largefile = true; +#else + bool largefile = false; +#endif + +#ifdef VFS_IGNORE_CASE + bool nocase = true; +#else + bool nocase = false; +#endif + return _checkCompatInternal(largefile, nocase, sizeof(vfspos)); +} +VFS_NAMESPACE_END + + +#include +#include +#include "VFSHelper.h" +#include "VFSFile.h" +#include "VFSDir.h" + + +// Checks to enforce correct including. +// At least on windows, includes , +// but that must be included after "VFSInternal.h", +// and "VFSInternal.h" may only be used inside the library (or by extensions), +// because it redefines fseek and ftell, which would +// mess up the ABI if included elsewhere. +#ifdef VFS_INTERNAL_H +#error Oops, VFS_INTERNAL_H is defined, someone messed up and included VFSInternal.h wrongly. +#endif + +#endif diff --git a/ExternalLibs/ttvfs/VFSAtomic.cpp b/ExternalLibs/ttvfs/VFSAtomic.cpp new file mode 100644 index 0000000..cb8d9da --- /dev/null +++ b/ExternalLibs/ttvfs/VFSAtomic.cpp @@ -0,0 +1,103 @@ +// VFSAtomic.cpp - atomic operations and thread locking +// For conditions of distribution and use, see copyright notice in VFS.h + +/** --- Atomic operations and thread safety --- + * You may want to add your own implementation if thread safety is needed. + * If not, just leave everything like it is. + + * If you are on windows, Interlocked[In/De]crement is faster than + explicit mutex locking for integer operations. + + * TODO: The actual locking that is done in the tree when VFS_THREADSAFE is defined + is rather crude for the time beeing; a somewhat more efficient ReadWriteLock + implementation would be nice to have, someday. + + * If you can, leave VFS_THREADSAFE undefined and do the locking externally, + it will probably have much better performance than if each and every operation + does a lock and unlock call. + (For a rather I/O based library this should not really make a difference, anyway. + But don't say you haven't been warned :) ) +*/ + +#include "VFSInternal.h" +#include "VFSAtomic.h" + +// for Interlocked[In/De]crement, if required +#if defined(_WIN32) && defined(VFS_THREADSAFE) +# define WIN32_LEAN_AND_MEAN +# include +#endif + +VFS_NAMESPACE_START + +#ifdef VFS_THREADSAFE +static Mutex mtx; +#endif + +int Atomic_Incr(volatile int &i) +{ +#ifdef VFS_THREADSAFE +# ifdef _WIN32 + volatile LONG* dp = (volatile LONG*) &i; + return InterlockedIncrement( dp ); +# else + Guard g(mtx); +# endif +#endif + return ++i; +} + +int Atomic_Decr(volatile int &i) +{ +#ifdef VFS_THREADSAFE +# ifdef _WIN32 + volatile LONG* dp = (volatile LONG*) &i; + return InterlockedDecrement( dp ); +# else + Guard g(mtx); +# endif +#endif + return --i; +} + +/* Implement your Mutex class here. + Important: The mutex must be re-entrant/recursive, + means it must be possible to lock it from the same thread multiple times. +*/ +Mutex::Mutex() +{ + // implement your own if needed. Remove the trap below when you are done. + // This is to prevent people from defining VFS_THREADSAFE and expecting everything to work just like that :) +#ifdef VFS_THREADSAFE +#error VFSAtomic: Hey, you forgot to implement the mutex class, cant guarantee thread safety! Either undef VFS_THREADSAFE or read the docs and get your hands dirty. +#endif +} + +Mutex::~Mutex() +{ + // implement your own if needed +} + +void Mutex::Lock(void) +{ + // implement your own if needed +} + +void Mutex::Unlock(void) +{ + // implement your own if needed +} + +Guard::Guard(Mutex& m) +: _m(m) +{ + _m.Lock(); +} + +Guard::~Guard() +{ + _m.Unlock(); +} + + +VFS_NAMESPACE_END \ No newline at end of file diff --git a/ExternalLibs/ttvfs/VFSAtomic.h b/ExternalLibs/ttvfs/VFSAtomic.h new file mode 100644 index 0000000..76665f0 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSAtomic.h @@ -0,0 +1,39 @@ +// VFSAtomic.h - atomic operations and thread locking +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFS_ATOMIC_H +#define VFS_ATOMIC_H + +#include "VFSDefines.h" + +VFS_NAMESPACE_START + +int Atomic_Incr(volatile int &i); +int Atomic_Decr(volatile int &i); + +// generic Mutex class, needs to be reentrant/recursive. +class Mutex +{ +public: + Mutex(); + ~Mutex(); + void Lock(); + void Unlock(); + +protected: + // add own stuff if needed +}; + +class Guard +{ +public: + Guard(Mutex& m); + ~Guard(); + +protected: + Mutex& _m; +}; + +VFS_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/VFSDefines.h b/ExternalLibs/ttvfs/VFSDefines.h new file mode 100644 index 0000000..14d8fef --- /dev/null +++ b/ExternalLibs/ttvfs/VFSDefines.h @@ -0,0 +1,79 @@ +// VFSDefines.h - compile config and basic setup +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFS_DEFINES_H +#define VFS_DEFINES_H + +/* --- Config section -- modify as needed --- */ + +// choose a namespace name, or comment out to disable namespacing completely (not recommended) +#define VFS_NAMESPACE ttvfs + +// Define this to allow dealing with files > 4 GB, using non-standard functions. +// This may or may not work with your platform/compiler, good luck. +//#define VFS_LARGEFILE_SUPPORT + +// Define this to make all operations case insensitive. +// Windows systems generally don't care much, but for Linux and Mac this can be used +// to get the same behavior as on windows. +// Additionally, this achieves full case insensitivity within the library, +// if the the same files are accessed multiple times by the program, but with not-uniform case. +// (no sane programmer should do this, anyway). +// However, on non-windows systems this will decrease performance when checking for files +// on disk (see VFSLoader.cpp). +#define VFS_IGNORE_CASE + +// Define this to make all VFSFile, VFSDir, VFSHelper operations thread-safe. +// If you do, do not forget to add your own implementation to VFSAtomic.cpp/.h ! +// If this is not defined, you can still do manual locking if you know what you're doing, +// performance matters, and you implemented actual locking into the Mutex class. +// If no Mutex implementation is provided, its operations are no-ops, beware! +//#define VFS_THREADSAFE + + +/* --- End of config section --- */ + + +#ifdef VFS_NAMESPACE +# define VFS_NAMESPACE_START namespace VFS_NAMESPACE { +# define VFS_NAMESPACE_END } +# define VFS_NAMESPACE_IMPL VFS_NAMESPACE:: +#else +# define VFS_NAMESPACE_START +# define VFS_NAMESPACE_END +# define VFS_NAMESPACE_IMPL +#endif + +VFS_NAMESPACE_START + +#ifdef VFS_LARGEFILE_SUPPORT +# if defined(_MSC_VER) + typedef __int64 vfspos; +# else + typedef long long vfspos; +# endif +#else + typedef unsigned int vfspos; +#endif + +// simple guard wrapper, works also if VFS_THREADSAFE is not defined +#define VFS_GUARD(obj) VFS_NAMESPACE_IMPL Guard __vfs_stack_guard((obj)->mutex()) + +// defines for optional auto-locking; only if VFS_THREADSAFE is defined +#ifdef VFS_THREADSAFE +# define VFS_GUARD_OPT(obj) VFS_GUARD(obj) +#else +# define VFS_GUARD_OPT(obj) +#endif + +#if defined(_MSC_VER) +# define VFS_STRICMP stricmp +#else +# define VFS_STRICMP strcasecmp +#endif + +static const vfspos npos = vfspos(-1); + +VFS_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/VFSDir.cpp b/ExternalLibs/ttvfs/VFSDir.cpp new file mode 100644 index 0000000..24cee4b --- /dev/null +++ b/ExternalLibs/ttvfs/VFSDir.cpp @@ -0,0 +1,255 @@ +// VFSDir.cpp - basic directory interface + classes +// For conditions of distribution and use, see copyright notice in VFS.h + +#include "VFSInternal.h" +#include "VFSTools.h" +#include "VFSFile.h" +#include "VFSDir.h" + +VFS_NAMESPACE_START + +VFSDir::VFSDir() +: ref(this), _name(NULL) +{ +} + +VFSDir::VFSDir(const char *fullpath) +: ref(this) +{ + _setFullName(fullpath); +} + +VFSDir::~VFSDir() +{ + for(Files::iterator it = _files.begin(); it != _files.end(); it++) + it->second->ref--; + for(Dirs::iterator it = _subdirs.begin(); it != _subdirs.end(); it++) + it->second->ref--; +} + +void VFSDir::_setFullName(const char *fullname) +{ + _fullname = FixPath(fullname); + _name = PathToFileName(_fullname.c_str()); +} + +VFSDir *VFSDir::createNew(void) const +{ + return new VFSDir; +} + +unsigned int VFSDir::load(const char *dir /* = NULL */) +{ + return 0; +} + + +bool VFSDir::add(VFSFile *f, bool overwrite /* = true */) +{ + if(!f) + return false; + + VFS_GUARD_OPT(this); + + Files::iterator it = _files.find(f->name()); + + if(it != _files.end()) + { + if(overwrite) + { + VFSFile *oldf = it->second; + if(oldf == f) + return false; + + oldf->ref--; + _files.erase(it); + } + else + return false; + } + + f->ref++; + _files[f->name()] = f; + return true; +} + +bool VFSDir::addRecursive(VFSFile *f, bool overwrite /* = true */) +{ + if(!f) + return false; + + VFS_GUARD_OPT(this); + + // figure out directory from full file name + std::string dirname(f->fullname()); + size_t pathend = dirname.find_last_of("/\\"); + VFSDir *vdir; + if(pathend != std::string::npos) + { + dirname = dirname.substr(0, pathend); + vdir = getDir(dirname.c_str(), true); + } + else + vdir = this; + + return vdir->add(f, true); +} + +bool VFSDir::merge(VFSDir *dir, bool overwrite /* = true */) +{ + if(!dir) + return false; + + bool result = false; + VFS_GUARD_OPT(this); + + for(Files::iterator it = dir->_files.begin(); it != dir->_files.end(); it++) + result = add(it->second, overwrite) || result; + + for(Dirs::iterator it = dir->_subdirs.begin(); it != dir->_subdirs.end(); it++) + result = insert(it->second, overwrite) || result; + return result; +} + +bool VFSDir::insert(VFSDir *subdir, bool overwrite /* = true */) +{ + if(!subdir) + return false; + + VFS_GUARD_OPT(this); + Dirs::iterator it = _subdirs.find(subdir->name()); + VFSDir *mydir; + if(it != _subdirs.end()) + { + mydir = it->second; + //return it->second->merge(subdir, overwrite); + } + else + { + // create a new subtree, not to pollute the original one with data that may be added later + mydir = subdir->createNew(); // create subdir of same type + mydir->_setFullName(subdir->fullname()); + _subdirs[mydir->name()] = mydir; + } + + return mydir->merge(subdir, overwrite); +} + +VFSFile *VFSDir::getFile(const char *fn) +{ + char *slashpos = (char *)strchr(fn, '/'); + + // if there is a '/' in the string, descend into subdir and continue there + if(slashpos) + { + const char *sub = slashpos + 1; + std::string t(fn, slashpos - fn); + VFS_GUARD_OPT(this); + VFSDir *subdir = getDir(t.c_str()); // fn is null-terminated early here + return subdir ? subdir->getFile(sub) : NULL; + } + + // no subdir? file must be in this dir now. + VFS_GUARD_OPT(this); + Files::iterator it = _files.find(fn); + return it != _files.end() ? it->second : NULL; +} + +VFSDir *VFSDir::getDir(const char *subdir, bool forceCreate /* = false */) +{ + if(!subdir[0] || (subdir[0] == '.' && (!subdir[1] || subdir[1] == '/'))) // empty string or "." or "./" ? use this. + return this; + + VFSDir *ret = NULL; + char *slashpos = (char *)strchr(subdir, '/'); + + // if there is a '/' in the string, descend into subdir and continue there + if(slashpos) + { + const char *sub = slashpos + 1; + std::string t(subdir, slashpos - subdir); + VFS_GUARD_OPT(this); + Dirs::iterator it = _subdirs.find(t); + if(it != _subdirs.end()) + { + ret = it->second->getDir(sub, forceCreate); // descend into subdirs + } + else if(forceCreate) + { + VFSDir *ins = createNew(); + std::string newname(fullname()); + newname += '/'; + newname += t; + ins->_setFullName(newname.c_str()); + _subdirs[ins->name()] = ins; + ret = ins->getDir(sub, true); // create remaining structure + } + } + else + { + VFS_GUARD_OPT(this); + Dirs::iterator it = _subdirs.find(subdir); + if(it != _subdirs.end()) + ret = it->second; + else if(forceCreate) + { + ret = createNew(); + std::string newname(fullname()); + newname += '/'; + newname += subdir; + ret->_setFullName(newname.c_str()); + _subdirs[ret->name()] = ret; + } + } + + return ret; +} + + + +// ----- VFSDirReal start here ----- + + +VFSDirReal::VFSDirReal() : VFSDir() +{ +} + +VFSDir *VFSDirReal::createNew(void) const +{ + return new VFSDirReal; +} + +unsigned int VFSDirReal::load(const char *dir /* = NULL */) +{ + VFS_GUARD_OPT(this); + if(dir) + _setFullName(dir); + + StringList li; + GetFileList(_fullname.c_str(), li); + for(StringList::iterator it = li.begin(); it != li.end(); it++) + { + if(VFSFile *oldf = getFile(it->c_str())) + oldf->ref--; + VFSFileReal *f = new VFSFileReal((_fullname + '/' + *it).c_str()); + _files[f->name()] = f; + } + unsigned int sum = li.size(); + + li.clear(); + GetDirList(_fullname.c_str(), li, false); + for(std::deque::iterator it = li.begin(); it != li.end(); it++) + { + if(VFSDir *oldd = getDir(it->c_str())) + oldd->ref--; + VFSDir *d = createNew(); + std::string full(_fullname); + full += '/'; + full += *it; + sum += d->load(full.c_str()); // GetDirList() always returns relative paths + _subdirs[d->name()] = d; + } + return sum; +} + +VFS_NAMESPACE_END diff --git a/ExternalLibs/ttvfs/VFSDir.h b/ExternalLibs/ttvfs/VFSDir.h new file mode 100644 index 0000000..2b86a38 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSDir.h @@ -0,0 +1,118 @@ +// VFSDir.h - basic directory interface + classes +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFSDIR_H +#define VFSDIR_H + +#include "VFSDefines.h" +#include +#include "VFSSelfRefCounter.h" + +VFS_NAMESPACE_START + +#ifdef VFS_IGNORE_CASE +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +# endif + +struct ci_less +{ + inline bool operator() (const std::string& a, const std::string& b) const + { + return VFS_STRICMP(a.c_str(), b.c_str()) < 0; + } +}; + +# ifdef _MSC_VER +# pragma warning(pop) +# endif +#endif + + +class VFSDir; +class VFSFile; + +class VFSDir +{ +public: + +#ifdef VFS_IGNORE_CASE + typedef std::map Dirs; + typedef std::map Files; +#else + typedef std::map Dirs; + typedef std::map Files; +#endif + + VFSDir(); + VFSDir(const char *fullpath); + virtual ~VFSDir(); + + /* Load directory with given path. If dir is NULL, reload previously loaded directory. + If there is no previously loaded directory, load root. */ + virtual unsigned int load(const char *dir = NULL); + virtual VFSFile *getFile(const char *fn); + virtual VFSDir *getDir(const char *subdir, bool forceCreate = false); + virtual VFSDir *createNew(void) const; + virtual const char *getType(void) const { return "VFSDir"; } + + bool insert(VFSDir *subdir, bool overwrite = true); + bool merge(VFSDir *dir, bool overwrite = true); + bool add(VFSFile *f, bool overwrite = true); // add file directly in this dir + bool addRecursive(VFSFile *f, bool overwrite = true); // traverse subdir tree to find correct subdir; create if not existing + + + inline const char *name() const { VFS_GUARD_OPT(this); return _name; } + inline const char *fullname() const { VFS_GUARD_OPT(this); return _fullname.c_str(); } + + // iterators are NOT thread-safe! If you need to iterate over things in a multithreaded environment, + // do the locking yourself! (see below) + inline Files::iterator fileIter() { return _files.begin(); } + inline Files::iterator fileIterEnd() { return _files.end(); } + inline Dirs::iterator dirIter() { return _subdirs.begin(); } + inline Dirs::iterator dirIterEnd() { return _subdirs.end(); } + inline Files::const_iterator fileIter() const { return _files.begin(); } + inline Files::const_iterator fileIterEnd() const { return _files.end(); } + inline Dirs::const_iterator dirIter() const { return _subdirs.begin(); } + inline Dirs::const_iterator dirIterEnd() const { return _subdirs.end(); } + + // std::map stores for files and subdirs + Files _files; + Dirs _subdirs; + + // reference counter, does auto-delete holder when it reaches 0. initially 1. + SelfRefCounter ref; + + // the following functions should be used before and after an iteration finishes + // alternatively, VFS_GUARD(dir) can be used to create a locking guard on the stack. + inline void lock() { _mtx.Lock(); } + inline void unlock() { _mtx.Unlock(); } + inline Mutex& mutex() const { return _mtx; } + +protected: + void _setFullName(const char *fullname); + std::string _fullname; + const char *_name; // must point to an address constant during object lifetime (like _fullname.c_str() + N) + // (not necessary to have an additional string copy here, just wastes memory) + mutable Mutex _mtx; +}; + +typedef VFSDir::Files::iterator FileIter; +typedef VFSDir::Dirs::iterator DirIter; +typedef VFSDir::Files::const_iterator ConstFileIter; +typedef VFSDir::Dirs::const_iterator ConstDirIter; + +class VFSDirReal : public VFSDir +{ +public: + VFSDirReal(); + virtual ~VFSDirReal() {}; + virtual unsigned int load(const char *dir = NULL); + virtual VFSDir *createNew(void) const; + virtual const char *getType(void) const { return "VFSDirReal"; } +}; + +VFS_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/VFSFile.cpp b/ExternalLibs/ttvfs/VFSFile.cpp new file mode 100644 index 0000000..7d7fbc5 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSFile.cpp @@ -0,0 +1,258 @@ +// VFSFile.cpp - basic file interface + classes +// For conditions of distribution and use, see copyright notice in VFS.h + +#include "VFSInternal.h" +#include "VFSFile.h" +#include "VFSTools.h" + +VFS_NAMESPACE_START + +VFSFile::VFSFile() +: ref(this) +{ +} + +VFSFileReal::VFSFileReal(const char *name /* = NULL */) +: VFSFile() +{ + _buf = NULL; + _setName(name); + _fh = NULL; + _size = npos; +} + +VFSFileReal::~VFSFileReal() +{ + close(); + dropBuf(true); +} + +// call this only with a lock held! +void VFSFileReal::_setName(const char *n) +{ + if(n && *n) + { + _fullname = FixPath(n); + _name = PathToFileName(_fullname.c_str()); + } +} + +bool VFSFileReal::open(const char *fn /* = NULL */, const char *mode /* = NULL */) +{ + VFS_GUARD_OPT(this); + + if(isopen()) + close(); + + dropBuf(true); + + _setName(fn); + + _fh = fopen(_fullname.c_str(), mode ? mode : "rb"); + if(!_fh) + return false; + + fseek((FILE*)_fh, 0, SEEK_END); + _size = getpos(); + fseek((FILE*)_fh, 0, SEEK_SET); + + return true; +} + +bool VFSFileReal::isopen(void) const +{ + VFS_GUARD_OPT(this); + return !!_fh; +} + +bool VFSFileReal::iseof(void) const +{ + VFS_GUARD_OPT(this); + return !_fh || feof((FILE*)_fh); +} + +const char *VFSFileReal::name(void) const +{ + VFS_GUARD_OPT(this); + return _name; +} + +const char *VFSFileReal::fullname(void) const +{ + VFS_GUARD_OPT(this); + return _fullname.c_str(); +} + +bool VFSFileReal::close(void) +{ + VFS_GUARD_OPT(this); + if(_fh) + { + fclose((FILE*)_fh); + _fh = NULL; + } + return true; +} + +bool VFSFileReal::seek(vfspos pos) +{ + VFS_GUARD_OPT(this); + if(!_fh) + return false; + fseek((FILE*)_fh, pos, SEEK_SET); + return true; +} + +bool VFSFileReal::seekRel(vfspos offs) +{ + VFS_GUARD_OPT(this); + if(!_fh) + return false; + fseek((FILE*)_fh, offs, SEEK_CUR); + return true; +} + +bool VFSFileReal::flush(void) +{ + VFS_GUARD_OPT(this); + if(_fh) + return false; + fflush((FILE*)_fh); + return true; +} + +vfspos VFSFileReal::getpos(void) const +{ + VFS_GUARD_OPT(this); + if(!_fh) + return npos; + return ftell((FILE*)_fh); +} + +unsigned int VFSFileReal::read(void *dst, unsigned int bytes) +{ + VFS_GUARD_OPT(this); + if(!_fh) + return npos; + return fread(dst, 1, bytes, (FILE*)_fh); +} + +unsigned int VFSFileReal::write(const void *src, unsigned int bytes) +{ + VFS_GUARD_OPT(this); + if(!_fh) + return npos; + return fwrite(src, 1, bytes, (FILE*)_fh); +} + +vfspos VFSFileReal::size(void) +{ + VFS_GUARD_OPT(this); + if(_size != npos) + return _size; + open(); + close(); + // now size is known. + return _size; +} + +const void *VFSFileReal::getBuf(void) +{ + VFS_GUARD_OPT(this); + if(_buf) + return _buf; + + bool op = isopen(); + + if(!op && !open()) // open with default params if not open + return NULL; + + unsigned int s = (unsigned int)size(); + _buf = malloc(s + 4); // a bit extra padding + if(!_buf) + return NULL; + + if(op) + { + vfspos oldpos = getpos(); + seek(0); + unsigned int offs = read(_buf, s); + memset((char*)_buf + offs, 0, 4); + seek(oldpos); + } + else + { + unsigned int offs = read(_buf, s); + memset((char*)_buf + offs, 0, 4); + close(); + } + return _buf; +} + +void VFSFileReal::dropBuf(bool del) +{ + VFS_GUARD_OPT(this); + if(del && _buf) + free(_buf); + _buf = NULL; +} + +// ------------- VFSFileMem ----------------------- + +VFSFileMem::VFSFileMem(const char *name, void *buf, unsigned int size, Mode mode /* = COPY */, delete_func delfunc /* = NULL */) +: VFSFile(), _pos(0), _size(size), _buf(buf), _delfunc(delfunc), _mybuf(mode == TAKE_OVER || mode == COPY) +{ + _setName(name); + if(mode == COPY) + { + _buf = malloc(size+1); + _delfunc = free; + memcpy(_buf, buf, size); + ((char*)_buf)[size] = 0; + } +} + +VFSFileMem::~VFSFileMem() +{ + if(_mybuf) + { + if(_delfunc) + _delfunc(_buf); + else + delete [] (char*)_buf; + } +} + + +void VFSFileMem::_setName(const char *n) +{ + if(n && *n) + { + _fullname = FixPath(n); + _name = PathToFileName(_fullname.c_str()); + } +} + +unsigned int VFSFileMem::read(void *dst, unsigned int bytes) +{ + VFS_GUARD_OPT(this); + if(iseof()) + return 0; + unsigned int rem = std::min((unsigned int)(_size - _pos), bytes); + + memcpy(dst, (char*)_buf + _pos, rem); + return rem; +} + +unsigned int VFSFileMem::write(const void *src, unsigned int bytes) +{ + VFS_GUARD_OPT(this); + if(iseof()) + return 0; + unsigned int rem = std::min((unsigned int)(_size - _pos), bytes); + + memcpy((char*)_buf + _pos, src, rem); + return rem; +} + +VFS_NAMESPACE_END diff --git a/ExternalLibs/ttvfs/VFSFile.h b/ExternalLibs/ttvfs/VFSFile.h new file mode 100644 index 0000000..ebc989a --- /dev/null +++ b/ExternalLibs/ttvfs/VFSFile.h @@ -0,0 +1,183 @@ +// VFSFile.h - basic file interface + classes +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFSFILE_H +#define VFSFILE_H + +#include "VFSDefines.h" +#include "VFSSelfRefCounter.h" +#include + +VFS_NAMESPACE_START + + +/** -- VFSFile basic interface -- + * All functions that return bool should return true on success and false on failure. + * If an operation is not necessary or irrelevant (for example, files in memory can't be closed), + * it is useful to return true anyways, because this operation did not fail, technically. + * (Common sense here!) + * An int/vfspos value of 0 indicates failure, except the size/seek/getpos functions, where npos means failure. + * Only the functions required or applicable need to be implemented, for unsupported operations + * the default implementation should be sufficient. + **/ +class VFSFile +{ +public: + + /** The ctor is expected to set both name() and fullname(). */ + VFSFile(); + + virtual ~VFSFile() {}; + + /** Open a file. If fn is NULL (the default), open fullname(). + Mode can be "r", "w", "rb", "rb", and possibly other things that fopen supports. + It is the subclass's choice to support other modes. Default is "rb". */ + virtual bool open(const char *fn = NULL, const char *mode = NULL) { return false; } + virtual bool isopen(void) const { return false; } + virtual bool iseof(void) const { return true; } + + /** Returns the plain file name. Never NULL. */ + virtual const char *name(void) const { return ""; } + + /** Returns the file name with full path. Never NULL. */ + virtual const char *fullname(void) const { return ""; } + + virtual bool close(void) { return true; } + virtual bool seek(vfspos pos) { return false; } + + /** Seek relative to current position. Negative numbers will seek backwards. + (In most cases, the default implementation does not have to be changed) */ + virtual bool seekRel(vfspos offs) { VFS_GUARD_OPT(this); return seek(getpos() + offs); } + + virtual bool flush(void) { return true; } + + /** Current offset in file. Return npos if NA. */ + virtual vfspos getpos(void) const { return npos; } + + virtual unsigned int read(void *dst, unsigned int bytes) { return 0; } + virtual unsigned int write(const void *src, unsigned int bytes) { return 0; } + + /** Return file size. If NA, return npos. If size is not yet known, + open() and close() may be called (with default args) to find out the size. + The file is supposed to be in its old state when the function returns, + that is in the same open state and seek position. + The pointer returned by getBuf() must not change. */ + virtual vfspos size(void) { return npos; } + + /** Attempt to increase file size. Returns new size after completion. + May return any size. Failure is indicated by a size() that didn't change. */ + virtual vfspos size(vfspos newsize) { return size(); } + + /** Return full file content in memory. Like size(), this may do other operations on the file, + but after the function returns the file is expected to be in the same state it was before. + If the file is not open before the call, it will be opened with default parameters (that is, "rb"). + Addition EOL mangling my happen if the file is opened in text mode before (= not binary). + Calls to open() should delete this memory if the file was previously opened in a different mode. + The returned memory is not guaranteed to be writable without problems, so don't do it. + Don't cast the const away. You have been warned. + This memory can be freed with free(), after calling dropBuf(false). */ + virtual const void *getBuf(void) { return NULL; } + + /** If del is true, delete internal buffer. If false, unregister internal buffer from the file, + but do not delete. Use free() later. */ + virtual void dropBuf(bool del) {} + + /** Basic RTTI, for debugging purposes */ + virtual const char *getType(void) const { return ""; } + + + /** Reference count, if the pointer to this file is stored somewhere it is advisable to increase + (ref++) it. If it reaches 0, this file is deleted automatically. */ + SelfRefCounter ref; + + + inline void lock() { _mtx.Lock(); } + inline void unlock() { _mtx.Unlock(); } + inline Mutex& mutex() const { return _mtx; } + +protected: + + mutable Mutex _mtx; +}; + +class VFSFileReal : public VFSFile +{ +public: + VFSFileReal(const char *name = NULL); + virtual ~VFSFileReal(); + virtual bool open(const char *fn = NULL, const char *mode = NULL); + virtual bool isopen(void) const; + virtual bool iseof(void) const; + virtual const char *name(void) const; + virtual const char *fullname(void) const; + virtual bool close(void); + virtual bool seek(vfspos pos); + virtual bool seekRel(vfspos offs); + virtual bool flush(void); + virtual vfspos getpos(void) const; + virtual unsigned int read(void *dst, unsigned int bytes); + virtual unsigned int write(const void *src, unsigned int bytes); + virtual vfspos size(void); + virtual const void *getBuf(void); + virtual void dropBuf(bool del); + virtual const char *getType(void) const { return "disk"; } + + inline void *getFP() { return _fh; } + +protected: + void _setName(const char *n); + std::string _fullname; + const char *_name; + void *_fh; // FILE* + vfspos _size; + void *_buf; +}; + +class VFSFileMem : public VFSFile +{ +public: + enum Mode + { + COPY, //- Make a copy of the buffer (default action). + REUSE, //- Use the passed-in buffer as is. Requires the pointer + // to remain valid over the life of this object. + TAKE_OVER, //- Take over the passed-in buffer; it will be deleted on object destruction. + }; + typedef void (*delete_func)(void*); + + /* Creates a virtual file from a memory buffer. By default, the memory is copied. + A deletor function can be passed optionally, if its NULL (the default), + delete[] (char*)buf will be used. For malloc()'d memory, pass free. (Only used if mode is TAKE_OVER) */ + VFSFileMem(const char *name, void *buf, unsigned int size, Mode m = COPY, delete_func delfunc = NULL); + virtual ~VFSFileMem(); + virtual bool open(const char *fn = NULL, const char *mode = NULL) { return true; } + virtual bool isopen(void) const { return true; } // always open + virtual bool iseof(void) const { VFS_GUARD_OPT(this); return _pos >= _size; } + virtual const char *name(void) const { VFS_GUARD_OPT(this); return _name; } + virtual const char *fullname(void) const { VFS_GUARD_OPT(this); return _fullname.c_str(); } + virtual bool close(void) { return true; } // cant close, but not a problem + virtual bool seek(vfspos pos) { VFS_GUARD_OPT(this); _pos = pos; return true; } + virtual bool seekRel(vfspos offs) { VFS_GUARD_OPT(this); _pos += offs; return true; } + virtual bool flush(void) { return false; } // can't flush, if a successful file write is expected, this IS a problem. + virtual vfspos getpos(void) const { VFS_GUARD_OPT(this); return _pos; } + virtual unsigned int read(void *dst, unsigned int bytes); + virtual unsigned int write(const void *src, unsigned int bytes); + virtual vfspos size(void) { VFS_GUARD_OPT(this); return _size; } + virtual const void *getBuf(void) { VFS_GUARD_OPT(this); return _buf; } + virtual void dropBuf(bool) {} // we can't simply drop the internal buffer, as the file is entirely memory based + virtual const char *getSource(void) const { return "mem"; } + +protected: + void _setName(const char *n); + std::string _fullname; + const char *_name; + vfspos _pos; + vfspos _size; + void *_buf; + delete_func _delfunc; + bool _mybuf; +}; + +VFS_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/VFSHelper.cpp b/ExternalLibs/ttvfs/VFSHelper.cpp new file mode 100644 index 0000000..70fabc2 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSHelper.cpp @@ -0,0 +1,350 @@ +// VFSHelper.cpp - glues it all together and makes use simple +// For conditions of distribution and use, see copyright notice in VFS.h + +#include // for debug only, see EOF + +#include "VFSInternal.h" +#include "VFSHelper.h" +#include "VFSAtomic.h" +#include "VFSTools.h" + +#include "VFSDir.h" +#include "VFSFile.h" +#include "VFSLoader.h" + +#ifdef _DEBUG +# include +# define DEBUG_ASSERT(x) assert(x) +#else +# define DEBUG_ASSERT(x) +#endif + + +VFS_NAMESPACE_START + +// predecl is in VFS.h +bool _checkCompatInternal(bool large, bool nocase, unsigned int vfspos_size) +{ +#ifdef VFS_LARGEFILE_SUPPORT + bool largefile_i = true; +#else + bool largefile_i = false; +#endif + +#ifdef VFS_IGNORE_CASE + bool nocase_i = true; +#else + bool nocase_i = false; +#endif + + return (large == largefile_i) + && (nocase == nocase_i) + && (sizeof(vfspos) == vfspos_size); +} + +VFSHelper::VFSHelper() +: filesysRoot(NULL), merged(NULL) +{ + _ldrDiskId = _AddFixedLoader(); // NULL intentionally. created by LoadFileSysRoot() +} + +VFSHelper::~VFSHelper() +{ + Clear(); +} + +void VFSHelper::Clear(void) +{ + VFS_GUARD_OPT(this); + _cleanup(); + + if(filesysRoot) + { + filesysRoot->ref--; // this should always delete it... + filesysRoot = NULL; // ...but it may be referenced elsewhere, just in case + } + + for(unsigned int i = 0; i < preRoot.size(); ++i) + preRoot[i]->ref--; + preRoot.clear(); + + for(unsigned int i = 0; i < postRoot.size(); ++i) + postRoot[i]->ref--; + postRoot.clear(); + + for(unsigned int i = 0; i < FixedLoadersCount(); ++i) + if(fixedLdrs[i]) + { + delete fixedLdrs[i]; + fixedLdrs[i] = NULL; + } +} + +unsigned int VFSHelper::_AddFixedLoader(VFSLoader *ldr /* = NULL */) +{ + fixedLdrs.push_back(ldr); + return FixedLoadersCount() - 1; +} + +void VFSHelper::_cleanup(void) +{ + VFS_GUARD_OPT(this); // be extra safe and ensure this is locked + if(merged) + { + merged->ref--; + merged = NULL; + } + for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it) + it->vdir->ref--; + vlist.clear(); + for(LoaderList::iterator it = dynLdrs.begin(); it != dynLdrs.end(); ++it) + delete *it; + dynLdrs.clear(); +} + +bool VFSHelper::LoadFileSysRoot(void) +{ + VFS_GUARD_OPT(this); + VFSDirReal *oldroot = filesysRoot; + + filesysRoot = new VFSDirReal; + if(!filesysRoot->load(".")) + { + filesysRoot->ref--; + filesysRoot = oldroot; + return false; + } + + if(!fixedLdrs[_ldrDiskId]) + fixedLdrs[_ldrDiskId] = new VFSLoaderDisk; + + if(oldroot) + oldroot->ref--; + + return true; +} + +void VFSHelper::Prepare(bool clear /* = true */) +{ + VFS_GUARD_OPT(this); + if(clear) + _cleanup(); + if(!merged) + { + merged = new VFSDir(""); + } + + for(size_t i = 0; i < preRoot.size(); ++i) + merged->merge(preRoot[i]); + if(filesysRoot) + merged->merge(filesysRoot); + for(size_t i = 0; i < postRoot.size(); ++i) + merged->merge(postRoot[i]); +} + +void VFSHelper::Reload(bool fromDisk /* = false */) +{ + VFS_GUARD_OPT(this); + if(fromDisk) + LoadFileSysRoot(); + Prepare(false); + for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); it++) + GetDir(it->mountPoint.c_str(), true)->merge(it->vdir, it->overwrite); +} + +bool VFSHelper::Mount(const char *src, const char *dest, bool overwrite /* = true*/) +{ + VFS_GUARD_OPT(this); + return AddVFSDir(GetDir(src, false), dest, overwrite); +} + +bool VFSHelper::AddVFSDir(VFSDir *dir, const char *subdir /* = NULL */, bool overwrite /* = true */) +{ + if(!dir) + return false; + VFS_GUARD_OPT(this); + if(!subdir) + subdir = dir->fullname(); + VFSDir *sd = GetDir(subdir, true); + if(!sd) // may be NULL if Prepare() was not called before + return false; + dir->ref++; // because this is to be added to vlist + VDirEntry ve(dir, subdir, overwrite); + _StoreMountPoint(ve); + sd->merge(dir, overwrite); // merge into specified subdir. will be (virtually) created if not existing + return true; +} + +bool VFSHelper::Unmount(const char *src, const char *dest) +{ + VFSDir *vd = GetDir(src, false); + if(!vd) + return false; + + VDirEntry ve(vd, dest, true); // last is dummy + if(!_RemoveMountPoint(ve)) + return false; + + Reload(false); + return true; +} + +void VFSHelper::_StoreMountPoint(const VDirEntry& ve) +{ + // scan through and ensure only one mount point with the same data is present. + // if present, remove and re-add, this ensures the mount point is at the end of the list + for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ) + { + const VDirEntry& oe = *it; + if(ve.mountPoint == oe.mountPoint && ve.vdir == oe.vdir + && (ve.overwrite || !oe.overwrite) ) // overwrite definitely, or if other does not overwrite + { + it = vlist.erase(it); // do not break; just in case there are more (fixme?) + } + else + ++it; + } + + vlist.push_back(ve); +} + +bool VFSHelper::_RemoveMountPoint(const VDirEntry& ve) +{ + for(VFSMountList::iterator it = vlist.begin(); it != vlist.end(); ++it) + { + const VDirEntry& oe = *it; + if(ve.mountPoint == oe.mountPoint && ve.vdir == oe.vdir) + { + vlist.erase(it); + return true; + } + } + return false; +} + +bool VFSHelper::MountExternalPath(const char *path, const char *where /* = "" */) +{ + // no guard required here, AddVFSDir has one, and the reference count is locked as well. + VFSDirReal *vfs = new VFSDirReal; + if(vfs->load(path)) + AddVFSDir(vfs, where); + return !!--(vfs->ref); // 0 if deleted +} + +void VFSHelper::AddLoader(VFSLoader *ldr) +{ + VFS_GUARD_OPT(this); + dynLdrs.push_back(ldr); +} + +inline static VFSFile *VFSHelper_GetFileByLoader(VFSLoader *ldr, const char *fn, VFSDir *root) +{ + if(!ldr) + return NULL; + VFSFile *vf = ldr->Load(fn); + if(vf) + { + root->addRecursive(vf, true); + --(vf->ref); + } + return vf; +} + +VFSFile *VFSHelper::GetFile(const char *fn) +{ + while(fn[0] == '.' && fn[1] == '/') + fn += 2; + + VFSFile *vf = NULL; + + // guarded block + { + VFS_GUARD_OPT(this); + + if(!merged) // Prepare() called? + return NULL; + + vf = merged->getFile(fn); + } + + // nothing found? maybe a loader has something. + // if so, add the newly created VFSFile to the tree. + // constant, no locking required here - also a bad idea in case a loader does heavy I/O + if(!vf) + for(unsigned int i = 0; i < fixedLdrs.size(); ++i) + if((vf = VFSHelper_GetFileByLoader(fixedLdrs[i], fn, GetDirRoot()))) + break; + + if(!vf) + { + VFS_GUARD_OPT(this); + for(LoaderList::iterator it = dynLdrs.begin(); it != dynLdrs.end(); ++it) + if((vf = VFSHelper_GetFileByLoader(*it, fn, GetDirRoot()))) + break; + } + + //printf("VFS: GetFile '%s' -> '%s' (%p)\n", fn, vf ? vf->fullname() : "NULL", vf); + + return vf; +} + +VFSDir *VFSHelper::GetDir(const char* dn, bool create /* = false */) +{ + while(dn[0] == '.' && dn[1] == '/') + dn += 2; + + VFS_GUARD_OPT(this); + return (merged && *dn) ? merged->getDir(dn, create) : merged; +} + +VFSDir *VFSHelper::GetDirRoot(void) +{ + VFS_GUARD_OPT(this); + return merged; +} + + +// DEBUG STUFF + +static void _DumpTreeRecursive(std::ostream& os, VFSDir *vd, const std::string& sp, VFSDir *parent) +{ + std::string sub = sp + " "; + + os << sp << "d|" << vd->name() << " [" << vd->getType() << ", ref " << vd->ref.count() << ", 0x" << vd << "]"; + + if(parent && strncmp(parent->fullname(), vd->fullname(), strlen(parent->fullname()))) + os << " <-- {" << vd->fullname() << "} ***********"; + os << std::endl; + + for(DirIter it = vd->_subdirs.begin(); it != vd->_subdirs.end(); ++it) + _DumpTreeRecursive(os, it->second, sub, vd); + + for(FileIter it = vd->_files.begin(); it != vd->_files.end(); ++it) + { + VFSFile *vf = it->second; + // only if refcount and/or mount point differs + bool p = false; + if(vf->ref.count() != vd->ref.count()) + { + doprint: + os << sub << "f|" << vf->name() << " [" << vf->getType() << ", ref " << vf->ref.count() << ", 0x" << vf << "]"; + p = true; + } + if(strncmp(vd->fullname(), vf->fullname(), strlen(vd->fullname()))) + { + if(!p) + goto doprint; + os << " <-- {" << vf->fullname() << "} ***********"; + } + if(p) + os << std::endl; + } +} + +void VFSHelper::debugDumpTree(std::ostream& os, VFSDir *start /* = NULL */) +{ + _DumpTreeRecursive(os, start ? start : GetDirRoot(), "", NULL); +} + + +VFS_NAMESPACE_END diff --git a/ExternalLibs/ttvfs/VFSHelper.h b/ExternalLibs/ttvfs/VFSHelper.h new file mode 100644 index 0000000..f7329c5 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSHelper.h @@ -0,0 +1,192 @@ +// VFSHelper.h - glues it all together and makes use simple +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFSHELPER_H +#define VFSHELPER_H + +#include +#include +#include +#include + +#include "VFSAtomic.h" + +VFS_NAMESPACE_START + +class VFSDir; +class VFSDirReal; +class VFSFile; +class VFSLoader; + + +/** VFSHelper - extensible class to simplify working with the VFS tree + * Contains a set of useful functions that should be useful for anyone. + * This class may be overridden to support adding any source in a comfortable way. + * + * Note: This class uses VFS_LAST_HELPER_CLASS, which should always store the last + * class derived from VFSHelper. This is supposed to make it easy to make extensions like this: + +#include "VFSHelperExtra.h" // defines a VFSHelperExtra class that is somehow derived from VFSHelper + // and follows the same rules as explained below. + +class VFSHelperArchive : public VFS_LAST_HELPER_CLASS +{ +private: + typedef VFS_LAST_HELPER_CLASS super; +public: + // .... class members .... +}; + +#undef VFS_LAST_HELPER_CLASS +#define VFS_LAST_HELPER_CLASS VFSHelperArchive + + + * Used this way, only the order in which VFSHelper extension classes are included matters. + * No code changes are required to get a nice inheritance and priority chain working. + * +*/ + +#ifdef VFS_LAST_HELPER_CLASS +# error VFS_LAST_HELPER_CLASS defined before VFSHelper class decl, check your include order! +#endif + +class VFSHelper +{ +public: + VFSHelper(); + virtual ~VFSHelper(); + + /** Creates the working tree. Required before any files or directories can be accessed. + Internally, it merges all individual VFS trees into one. If clear is true (default), + an existing merged tree is dropped, and old and previously added files and loaders removed. + (This is the recommended setting.) */ + virtual void Prepare(bool clear = true); + + /** Re-merges any files in the tree, and optionally reloads files on disk. + This is useful if files on disk were created or removed, and the tree needs to reflect these changes. */ + virtual void Reload(bool fromDisk = false); + + /** Reset an instance to its initial state */ + virtual void Clear(void); + + /** Load all files from working directory (into an internal tree) */ + bool LoadFileSysRoot(void); + + /** Mount a directory in the tree to a different location. Requires a previous call to Prepare(). + This can be imagined like a symlink pointing to a different location. + Be careful not to create circles, this might technically work, + but confuses the reference counting, causing memory leaks. */ + bool Mount(const char *src, const char *dest, bool overwrite = true); + + /** Drops a directory from the tree. Internally, this calls Reload(false), + which is a heavy operation compared to Mount(). Be warned. */ + bool Unmount(const char *src, const char *dest); + + /** Merges a path into the tree. Requires a previous call to Prepare(). + By default the directory is added into the root directory of the merged tree. + Pass NULL to add the directory to its original location, + or any other path to add it to that explicit location. + It is advised not to use this to re-add parts already in the tree; use Mount() instead. + Rule of thumb: If you called LoadFileSysRoot(), do not use this for subdirs. */ + bool MountExternalPath(const char *path, const char *where = ""); + + /** Adds a VFSDir object into the merged tree. If subdir is NULL (the default), + add into the subdir stored in the VFSDir object. The tree will be extended if target dir does not exist. + If overwrite is true (the default), files in the tree will be replaced if already existing. + Requires a previous call to Prepare(). + Like with Mount(); be careful not to create cycles. */ + bool AddVFSDir(VFSDir *dir, const char *subdir = NULL, bool overwrite = true); + + /** Add a loader that can look for files on demand. + It will be deleted if Prepare(true) is called. + It is possible (but not a good idea) to add a loader multiple times. */ + inline void AddLoader(VFSLoader *ldr); + + /** Get a file from the merged tree. Requires a previous call to Prepare(). + Asks loaders if the file is not in the tree. If found by a loader, the file will be added to the tree. + The returned pointer is reference counted. In case the file pointer is stored elsewhere, + do ptr->ref++, and later ptr->ref--. This is to prevent the VFS tree from deleting the file when cleaning up. + Not necessary if the pointer is just retrieved and used, or temp. stored while the VFS tree is not modified. */ + VFSFile *GetFile(const char *fn); + + /** Get a directory from the merged tree. If create is true and the directory does not exist, + build the tree structure and return the newly created dir. NULL otherwise. + Requires a previous call to Prepare(). + Reference counted, same as GetFile(), look there for more info. */ + VFSDir *GetDir(const char* dn, bool create = false); + + /** Returns the tree root, which is usually the working directory. */ + VFSDir *GetDirRoot(void); + + /** Remove a file or directory from the tree */ + //bool Remove(VFSFile *vf); + //bool Remove(VFSDir *dir); + //bool Remove(const char *name); // TODO: CODE ME + + /** This depends on the class type and stays constant. */ + inline unsigned int FixedLoadersCount(void) const { return (unsigned int)fixedLdrs.size(); } + + inline void lock() { _mtx.Lock(); } + inline void unlock() { _mtx.Unlock(); } + inline Mutex& mutex() const { return _mtx; } + + // DEBUG STUFF + void debugDumpTree(std::ostream& os, VFSDir *start = NULL); + +protected: + + /** Drops the merged tree and additional mount points and dynamic loaders. + Overload to do additional cleanup if required. Invoked by Clear() and Prepare(true). */ + virtual void _cleanup(void); + + /** Add a fixed VFSLoader. Returns its array index in fixedLdrs. */ + unsigned int _AddFixedLoader(VFSLoader *ldr = NULL); + + struct VDirEntry + { + VDirEntry() : vdir(NULL), overwrite(false) {} + VDirEntry(VFSDir *v, std::string mp, bool ow) : vdir(v), mountPoint(mp), overwrite(ow) {} + VFSDir *vdir; + std::string mountPoint; + bool overwrite; + }; + + typedef std::list VFSMountList; + typedef std::vector LoaderArray; + typedef std::deque LoaderList; + typedef std::vector DirArray; + + + void _StoreMountPoint(const VDirEntry& ve); + + bool _RemoveMountPoint(const VDirEntry& ve); + + // the VFSDirs are merged in their declaration order. + // when merging, files already contained can be overwritten by files merged in later. + VFSDirReal *filesysRoot; // local files on disk (root dir) + + // Additional tree stores, to be filled by subclasses if needed. + DirArray preRoot; // VFSDirs in here will be merged in, before the actual disk files. + // Means files on disk will overwrite existing entries. + DirArray postRoot; // Will be merged after the disk files, and overwrite prev. merged files. + // Both may contain NULLs. + + // if files are not in the tree, maybe one of these is able to find it. May contain NULLs. + LoaderArray fixedLdrs; // defined by class type, stays constant during object lifetime + LoaderList dynLdrs; // dynamically added on demand, deleted on _cleanup() + + VFSDir *merged; // contains the merged virtual/actual file system tree + + mutable Mutex _mtx; + +private: + unsigned int _ldrDiskId; + VFSMountList vlist; // all other dirs added later, together with path to mount to +}; + +#undef VFS_LAST_HELPER_CLASS +#define VFS_LAST_HELPER_CLASS VFSHelper + +VFS_NAMESPACE_END + +#endif diff --git a/ExternalLibs/ttvfs/VFSInternal.h b/ExternalLibs/ttvfs/VFSInternal.h new file mode 100644 index 0000000..0942dbb --- /dev/null +++ b/ExternalLibs/ttvfs/VFSInternal.h @@ -0,0 +1,43 @@ +// VFSInternal.h - misc things that are not required to be visible outside of the library. +// For conditions of distribution and use, see copyright notice in VFS.h + +// !! this file is supposed to be included ONLY from VFS*.cpp files. + +#ifndef VFS_INTERNAL_H +#define VFS_INTERNAL_H + +// checks to enforcecorrect including +#ifdef TTVFS_VFS_H +#error Oops, TTVFS_VFS_H is defined, someone messed up and included VFS.h wrongly. +#endif + +#include "VFSDefines.h" + +#if _MSC_VER +# define _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_DEPRECATE +# pragma warning(disable: 4355) // 'this' : used in base member initializer list +#endif + +// this is for POSIX - define before including any stdio headers +#ifdef VFS_LARGEFILE_SUPPORT +# ifndef _MSC_VER +# define _FILE_OFFSET_BITS 64 +# endif +#endif + + +#include +#include +#include +#include + +// this is for MSVC - re-define functions +#ifdef VFS_LARGEFILE_SUPPORT +# ifdef _MSC_VER +# define fseek _fseeki64 +# define ftell _ftelli64 +# endif +#endif + +#endif diff --git a/ExternalLibs/ttvfs/VFSLoader.cpp b/ExternalLibs/ttvfs/VFSLoader.cpp new file mode 100644 index 0000000..7ae72ae --- /dev/null +++ b/ExternalLibs/ttvfs/VFSLoader.cpp @@ -0,0 +1,102 @@ +// VFSLoader.cpp - late loading of files not in the tree +// For conditions of distribution and use, see copyright notice in VFS.h + +#include "VFSInternal.h" +#include "VFSTools.h" +#include "VFSFile.h" +#include "VFSLoader.h" + +VFS_NAMESPACE_START + +#if !defined(_WIN32) && defined(VFS_IGNORE_CASE) + +#include + +// based on code in PhysicsFS: http://icculus.org/physfs/ +static bool locateOneElement(char *buf) +{ + char *ptr; + DIR *dirp; + + ptr = strrchr(buf, '/'); // find entry at end of path. + if (ptr == NULL) + { + dirp = opendir("."); + ptr = buf; + } + else + { + *ptr = '\0'; + dirp = opendir(buf); + *ptr = '/'; + ptr++; // point past dirsep to entry itself. + } + + struct dirent *dent; + while ((dent = readdir(dirp)) != NULL) + { + if (strcasecmp(dent->d_name, ptr) == 0) + { + strcpy(ptr, dent->d_name); // found a match. Overwrite with this case. + closedir(dirp); + return true; + } + } + + // no match at all... + closedir(dirp); + return false; +} + +static bool findFileHarder(char *fn) +{ + char *ptr = fn; + bool found = true; + while ((ptr = strchr(ptr + 1, '/')) != 0) + { + *ptr = '\0'; + found = locateOneElement(fn); + *ptr = '/'; // restore path separator + if (!found) + return false; + } + + // check final element... + found = found && locateOneElement(fn); + + printf("tt: Fixed case '%s' [%s]\n", fn, found ? "found" : "NOT FOUND"); // TEMP + return found; +} +#endif + + +VFSFile *VFSLoaderDisk::Load(const char *fn) +{ + if(FileExists(fn)) + return new VFSFileReal(fn); // must contain full file name + +#if !defined(_WIN32) && defined(VFS_IGNORE_CASE) + size_t s = strlen(fn); + if(s < 511) // avoid using malloc() and alloca() for short strings + { + char t[512]; + memcpy(&t[0], fn, s+1); // copy terminating '\0' as well + if(findFileHarder(&t[0])) // fixes the filename on the way + return new VFSFileReal(&t[0]); + } + else + { + char *t = (char*)malloc(s+1); + VFSFileReal *vf = NULL; + memcpy(t, fn, s+1); + if(findFileHarder(&t[0])) + vf = new VFSFileReal(&t[0]); + free(t); + return vf; + } +#endif + + return NULL; +} + +VFS_NAMESPACE_END diff --git a/ExternalLibs/ttvfs/VFSLoader.h b/ExternalLibs/ttvfs/VFSLoader.h new file mode 100644 index 0000000..c735601 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSLoader.h @@ -0,0 +1,31 @@ +// VFSLoader.h - late loading of files not in the tree +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFSLOADER_H +#define VFSLOADER_H + +#include "VFSDefines.h" + +VFS_NAMESPACE_START + +class VFSFile; + +// VFSLoader - to be called if a file is not in the tree. +class VFSLoader +{ +public: + virtual ~VFSLoader() {} + virtual VFSFile *Load(const char *fn) = 0; +}; + +class VFSLoaderDisk : public VFSLoader +{ +public: + virtual ~VFSLoaderDisk() {} + virtual VFSFile *Load(const char *fn); +}; + +VFS_NAMESPACE_END + + +#endif \ No newline at end of file diff --git a/ExternalLibs/ttvfs/VFSSelfRefCounter.h b/ExternalLibs/ttvfs/VFSSelfRefCounter.h new file mode 100644 index 0000000..42cfa38 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSSelfRefCounter.h @@ -0,0 +1,44 @@ +#ifndef SELFREFCOUNTER_H +#define SELFREFCOUNTER_H + +#include "VFSDefines.h" +#include "VFSAtomic.h" + +VFS_NAMESPACE_START + +// self must point to the object that holds the counter. +template class SelfRefCounter +{ +private: + T *self; + volatile int c; + SelfRefCounter(SelfRefCounter& r); // forbid copy constructor + inline unsigned int _deref(void) + { + unsigned int cc = (unsigned int)Atomic_Decr(c); // copy c, in case we get deleted + if(DELSELF && !cc) + { + delete self; + } + + return cc; + } + +public: + SelfRefCounter(T *p): self(p), c(1) {} + ~SelfRefCounter() { /* DEBUG(ASSERT(c <= 1)); */ } // its ok if the last reference calls delete instead of _deref() + inline unsigned int count(void) { return c; } + + // post-increment (dummy int) + inline unsigned int operator++(int) { unsigned int cc = c; Atomic_Incr(c); return cc; } + inline unsigned int operator--(int) { unsigned int cc = c; _deref(); return cc; } + + // pre-increment + inline unsigned int operator++(void) { return (unsigned int)Atomic_Incr(c); } + inline unsigned int operator--(void) { return _deref(); } +}; + +VFS_NAMESPACE_END + + +#endif diff --git a/ExternalLibs/ttvfs/VFSTools.cpp b/ExternalLibs/ttvfs/VFSTools.cpp new file mode 100644 index 0000000..0d49945 --- /dev/null +++ b/ExternalLibs/ttvfs/VFSTools.cpp @@ -0,0 +1,427 @@ +// VFSTools.cpp - useful functions and misc stuff +// For conditions of distribution and use, see copyright notice in VFS.h + +#include "VFSInternal.h" + +#include +#include +#include +#include +#include "VFSTools.h" + + +#if _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#else +# include +# include +# include +# include +#endif + +VFS_NAMESPACE_START + +std::string stringToLower(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), tolower); + return s; +} + +std::string stringToUpper(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), toupper); + return s; +} + +void makeLowercase(std::string& s) +{ + std::transform(s.begin(), s.end(), s.begin(), tolower); +} + +void makeUppercase(std::string& s) +{ + std::transform(s.begin(), s.end(), s.begin(), toupper); +} + +// returns list of *plain* file names in given directory, +// without paths, and without anything else +void GetFileList(const char *path, StringList& files) +{ +#if !_WIN32 + DIR * dirp; + struct dirent * dp; + dirp = opendir(path); + if(dirp) + { + while((dp=readdir(dirp)) != NULL) + { + if (dp->d_type != DT_DIR) // only add if it is not a directory + { + std::string s(dp->d_name); + files.push_back(s); + } + } + closedir(dirp); + } + +# else + + WIN32_FIND_DATA fil; + std::string search(path); + MakeSlashTerminated(search); + search += "*"; + HANDLE hFil = FindFirstFile(search.c_str(),&fil); + if(hFil != INVALID_HANDLE_VALUE) + { + do + { + if(!(fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + std::string s(fil.cFileName); + files.push_back(s); + } + } + while(FindNextFile(hFil, &fil)); + + FindClose(hFil); + } + +# endif +} + +// returns a list of directory names in the given directory, *without* the source dir. +// if getting the dir list recursively, all paths are added, except *again* the top source dir beeing queried. +void GetDirList(const char *path, StringList &dirs, bool recursive /* = false */) +{ +#if !_WIN32 + DIR * dirp; + struct dirent * dp; + dirp = opendir(path); + if(dirp) + { + while((dp = readdir(dirp))) // assignment is intentional + { + if (dp->d_type == DT_DIR) // only add if it is a directory + { + if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) + { + dirs.push_back(dp->d_name); + if (recursive) // needing a better way to do that + { + std::deque newdirs; + GetDirList(dp->d_name, newdirs, true); + std::string d(dp->d_name); + for(std::deque::iterator it = newdirs.begin(); it != newdirs.end(); ++it) + dirs.push_back(d + *it); + } + } + } + } + closedir(dirp); + } + +#else + + std::string search(path); + MakeSlashTerminated(search); + search += "*"; + WIN32_FIND_DATA fil; + HANDLE hFil = FindFirstFile(search.c_str(),&fil); + if(hFil != INVALID_HANDLE_VALUE) + { + do + { + if( fil.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + if (!strcmp(fil.cFileName, ".") || !strcmp(fil.cFileName, "..")) + continue; + + std::string d(fil.cFileName); + dirs.push_back(d); + + if (recursive) // need a better way to do that + { + StringList newdirs; + GetDirList(d.c_str(), newdirs, true); + + for(std::deque::iterator it = newdirs.begin(); it != newdirs.end(); ++it) + dirs.push_back(d + *it); + } + } + } + while(FindNextFile(hFil, &fil)); + + FindClose(hFil); + } + +#endif +} + +bool FileExists(const char *fn) +{ +#ifdef _WIN32 + FILE *fp = fopen(fn, "rb"); + if(fp) + { + fclose(fp); + return true; + } + return false; +#else + return access(fn, F_OK) == 0; +#endif +} + +// must return true if creating the directory was successful, or already exists +bool CreateDir(const char *dir) +{ + if(IsDirectory(dir)) // do not try to create if it already exists + return true; + bool result; +# if _WIN32 + result = !!::CreateDirectory(dir, NULL); +# else + result = !mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#endif + return result; +} + +bool CreateDirRec(const char *dir) +{ + if(IsDirectory(dir)) + return true; + bool result = true; + StringList li; + StrSplit(dir, "/\\", li, false); + std::string d; + d.reserve(strlen(dir)); + bool last; + for(StringList::iterator it = li.begin(); it != li.end(); it++) + { + d += *it; + last = CreateDir(d.c_str()); + result = last && result; + d += '/'; + } + return result || last; +} + +vfspos GetFileSize(const char* fn) +{ + if(!fn || !*fn) + return 0; + FILE *fp = fopen(fn, "rb"); + if(!fp) + return 0; + fseek(fp, 0, SEEK_END); + vfspos s = (vfspos)ftell(fp); + fclose(fp); + + return s == npos ? 0 : s; +} + +std::string FixSlashes(const std::string& s) +{ + std::string r; + r.reserve(s.length() + 1); + char last = 0, cur; + for(size_t i = 0; i < s.length(); ++i) + { + cur = s[i]; + if(cur == '\\') + cur = '/'; + if(last == '/' && cur == '/') + continue; + r += cur; + last = cur; + } + return r; +} + +std::string FixPath(const std::string& s) +{ + const char *p = s.c_str(); + while(p[0] == '.' && p[1] == '/') + p += 2; + return FixSlashes(p == s.c_str() ? s : p); // avoid hidden re-allocation when pointer was not moved +} + +bool IsDirectory(const char *s) +{ +#if _WIN32 + DWORD dwFileAttr = GetFileAttributes(s); + if(dwFileAttr == INVALID_FILE_ATTRIBUTES) + return false; + return !!(dwFileAttr & FILE_ATTRIBUTE_DIRECTORY); +#else + if ( access( s, 0 ) == 0 ) + { + struct stat status; + stat( s, &status ); + return status.st_mode & S_IFDIR; + } + return false; +#endif +} + +void MakeSlashTerminated(std::string& s) +{ + if(s.length() && s[s.length() - 1] != '/') + s += '/'; +} + +// extracts the file name from a given path +const char *PathToFileName(const char *str) +{ + const char *p = strrchr(str, '/'); + return p ? p+1 : str; +} + +std::string StripFileExtension(const std::string& s) +{ + size_t pos = s.find_last_of('.'); + size_t pos2 = s.find_last_of('/'); + if(pos != std::string::npos && (pos2 < pos || pos2 == std::string::npos)) + return s.substr(0, pos); + + return s; +} + +std::string StripLastPath(const std::string& s) +{ + if(s[s.length() - 1] == '/') + return StripLastPath(s.substr(0, s.length() - 1)); + + size_t pos = s.find_last_of('/'); + if(pos == std::string::npos) + return ""; // nothing remains + + return s.substr(0, pos); +} + +void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir /* = false */) +{ + std::stack stk; + + if(withQueriedDir) + { + stk.push(dir); + while(stk.size()) + { + dir = stk.top(); + stk.pop(); + MakeSlashTerminated(dir); + + StringList li; + GetFileList(dir.c_str(), li); + for(std::deque::iterator it = li.begin(); it != li.end(); ++it) + files.push_back(dir + *it); + + li.clear(); + GetDirList(dir.c_str(), li, true); + for(std::deque::iterator it = li.begin(); it != li.end(); ++it) + stk.push(dir + *it); + } + } + else + { + std::string topdir = dir; + MakeSlashTerminated(topdir); + stk.push(""); + while(stk.size()) + { + dir = stk.top(); + stk.pop(); + MakeSlashTerminated(dir); + + StringList li; + dir = topdir + dir; + GetFileList(dir.c_str(), li); + for(std::deque::iterator it = li.begin(); it != li.end(); ++it) + files.push_back(dir + *it); + + li.clear(); + GetDirList(dir.c_str(), li, true); + for(std::deque::iterator it = li.begin(); it != li.end(); ++it) + stk.push(dir + *it); + } + } +} + +// from http://board.byuu.org/viewtopic.php?f=10&t=1089&start=15 +bool WildcardMatch(const char *str, const char *pattern) +{ + const char *cp = 0, *mp = 0; + while(*str && *pattern != '*') + { + if(*pattern != *str && *pattern != '?') + return false; + pattern++, str++; + } + + while(*str) + { + if(*pattern == '*') + { + if(!*++pattern) + return 1; + mp = pattern; + cp = str + 1; + } + else if(*pattern == *str || *pattern == '?') + { + ++pattern; + ++str; + } + else + { + pattern = mp; + str = cp++; + } + } + + while(*pattern++ == '*'); + + return !*pattern; +} + +// copy strings, mangling newlines to system standard +// windows has 13+10 +// *nix has 10 +// exotic systems may have 10+13 +size_t strnNLcpy(char *dst, const char *src, unsigned int n /* = -1 */) +{ + char *olddst = dst; + bool had10 = false, had13 = false; + + --n; // reserve 1 for \0 at end + + while(*src && n) + { + if((had13 && *src == 10) || (had10 && *src == 13)) + { + ++src; // last was already mangled + had13 = had10 = false; // processed one CRLF pair + continue; + } + had10 = *src == 10; + had13 = *src == 13; + + if(had10 || had13) + { + *dst++ = '\n'; + ++src; + } + else + *dst++ = *src++; + + --n; + } + + *dst++ = 0; + + return dst - olddst; +} + +VFS_NAMESPACE_END diff --git a/ExternalLibs/ttvfs/VFSTools.h b/ExternalLibs/ttvfs/VFSTools.h new file mode 100644 index 0000000..369c8bb --- /dev/null +++ b/ExternalLibs/ttvfs/VFSTools.h @@ -0,0 +1,59 @@ +// VFSTools.h - useful functions and misc stuff +// For conditions of distribution and use, see copyright notice in VFS.h + +#ifndef VFS_TOOLS_H +#define VFS_TOOLS_H + +#include +#include + +#include "VFSDefines.h" + +VFS_NAMESPACE_START + +typedef std::deque StringList; + +std::string stringToUpper(const std::string& s); +std::string stringToLower(const std::string& s); +void makeUppercase(std::string& s); +void makeLowercase(std::string& s); +void GetFileList(const char *, StringList& files); +void GetDirList(const char *, StringList& dirs, bool recursive = false); +bool FileExists(const char *); +bool IsDirectory(const char *); +bool CreateDir(const char*); +bool CreateDirRec(const char*); +vfspos GetFileSize(const char*); +std::string FixSlashes(const std::string& s); +std::string FixPath(const std::string& s); +const char *PathToFileName(const char *str); +void MakeSlashTerminated(std::string& s); +std::string StripFileExtension(const std::string& s); +std::string StripLastPath(const std::string& s); +void GetFileListRecursive(std::string dir, StringList& files, bool withQueriedDir = false); +bool WildcardMatch(const char *str, const char *pattern); +size_t strnNLcpy(char *dst, const char *src, unsigned int n = -1); + +template void StrSplit(const std::string &src, const std::string &sep, T& container, bool keepEmpty = false) +{ + std::string s; + for (std::string::const_iterator i = src.begin(); i != src.end(); i++) + { + if (sep.find(*i) != std::string::npos) + { + if (keepEmpty || s.length()) + container.push_back(s); + s = ""; + } + else + { + s += *i; + } + } + if (keepEmpty || s.length()) + container.push_back(s); +} + +VFS_NAMESPACE_END + +#endif