From d2ae847ecf7b238bcb14bc4e08647a2d60d9c31f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 8 Mar 2014 16:05:23 +0000 Subject: [PATCH] - files in local directories (saves & configs) now always have higher priority than mods. Fixes #1685 and #1733 - fixed possible crash on exit in dispose() function - (vcmibuilder) fixes problem with partial mp3 -> ogg conversion --- client/CGameInfo.cpp | 4 +- client/CMT.cpp | 7 ++- client/CMessage.cpp | 6 +-- client/CPreGame.cpp | 6 +-- client/Client.cpp | 8 +-- client/NetPacksClient.cpp | 2 +- lib/CConfigHandler.cpp | 12 ++--- lib/CModHandler.cpp | 8 +-- lib/filesystem/Filesystem.cpp | 92 +++++++++++++++++++++-------------- lib/filesystem/Filesystem.h | 11 +++-- server/CGameHandler.cpp | 8 +-- server/CVCMIServer.cpp | 4 +- vcmibuilder | 2 +- 13 files changed, 97 insertions(+), 73 deletions(-) diff --git a/client/CGameInfo.cpp b/client/CGameInfo.cpp index c144d47d4..06786663f 100644 --- a/client/CGameInfo.cpp +++ b/client/CGameInfo.cpp @@ -14,7 +14,7 @@ */ const CGameInfo * CGI; //game info for general use -CClientState * CCS; +CClientState * CCS = nullptr; CGameInfo::CGameInfo() { @@ -32,4 +32,4 @@ void CGameInfo::setFromLib() objh = VLC->objh; spellh = VLC->spellh; dobjinfo = VLC->dobjinfo; -} \ No newline at end of file +} diff --git a/client/CMT.cpp b/client/CMT.cpp index c1ce226ba..eac2ab75f 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -751,8 +751,11 @@ void dispose() // cleanup, mostly to remove false leaks from analyzer CResourceHandler::clear(); - CCS->musich->release(); - CCS->soundh->release(); + if (CCS) + { + CCS->musich->release(); + CCS->soundh->release(); + } CMessage::dispose(); } diff --git a/client/CMessage.cpp b/client/CMessage.cpp index db22207f4..d51528d51 100644 --- a/client/CMessage.cpp +++ b/client/CMessage.cpp @@ -63,7 +63,7 @@ struct ComponentsToBlit namespace { - CDefHandler * ok, *cancel; + CDefHandler * ok = nullptr, *cancel = nullptr; std::vector > piecesOfBox; //in colors of all players SDL_Surface * background = nullptr; } @@ -100,9 +100,9 @@ void CMessage::init() void CMessage::dispose() { - for (int i=0; icb.get(), saveGameName); overWrite += boost::bind(&CGuiHandler::popIntTotally, &GH, this); - if(CResourceHandler::get()->existsResource(ResourceID(saveGameName, EResType::CLIENT_SAVEGAME))) + if(CResourceHandler::get("local")->existsResource(ResourceID(saveGameName, EResType::CLIENT_SAVEGAME))) { std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite? boost::algorithm::replace_first(hlp, "%s", sel->txt->text); @@ -1354,7 +1354,7 @@ void SelectionTab::select( int position ) if(txt) { - std::string filename = *CResourceHandler::get()->getResourceName( + std::string filename = *CResourceHandler::get("local")->getResourceName( ResourceID(curItems[py]->fileURI, EResType::CLIENT_SAVEGAME)); txt->setText(CFileInfo(filename).getBaseName()); } @@ -1479,7 +1479,7 @@ void SelectionTab::printMaps(SDL_Surface *to) } else { - name = CFileInfo(*CResourceHandler::get()->getResourceName( + name = CFileInfo(*CResourceHandler::get("local")->getResourceName( ResourceID(currentItem->fileURI, EResType::CLIENT_SAVEGAME))).getBaseName(); } diff --git a/client/Client.cpp b/client/Client.cpp index 22cd0878b..d0e9bba1b 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -241,17 +241,17 @@ void CClient::loadGame( const std::string & fname ) CStopWatch tmh; try { - std::string clientSaveName = *CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)); + std::string clientSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::CLIENT_SAVEGAME)); std::string controlServerSaveName; - if (CResourceHandler::get()->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME))) + if (CResourceHandler::get("local")->existsResource(ResourceID(fname, EResType::SERVER_SAVEGAME))) { - controlServerSaveName = *CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)); + controlServerSaveName = *CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)); } else// create entry for server savegame. Triggered if save was made after launch and not yet present in res handler { controlServerSaveName = clientSaveName.substr(0, clientSaveName.find_last_of(".")) + ".vsgm1"; - CResourceHandler::get()->createResource(controlServerSaveName, true); + CResourceHandler::get("local")->createResource(controlServerSaveName, true); } if(clientSaveName.empty()) diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 73695a237..48a27020c 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -793,7 +793,7 @@ void YourTurn::applyCl( CClient *cl ) void SaveGame::applyCl(CClient *cl) { CFileInfo info(fname); - CResourceHandler::get()->createResource(info.getStem() + ".vcgm1"); + CResourceHandler::get("local")->createResource(info.getStem() + ".vcgm1"); try { diff --git a/lib/CConfigHandler.cpp b/lib/CConfigHandler.cpp index 81d757fc8..4aa4036a6 100644 --- a/lib/CConfigHandler.cpp +++ b/lib/CConfigHandler.cpp @@ -59,11 +59,11 @@ void SettingsStorage::init() { std::string confName = "config/settings.json"; - // Porbably new install. Create initial configuration - if (!CResourceHandler::get()->existsResource(ResourceID(confName))) - CResourceHandler::get()->createResource(confName); - else - JsonNode(ResourceID("config/settings.json")).swap(config); + JsonUtils::assembleFromFiles(confName).swap(config); + + // Probably new install. Create config file to save settings to + if (!CResourceHandler::get("local")->existsResource(ResourceID(confName))) + CResourceHandler::get("local")->createResource(confName); JsonUtils::maximize(config, "vcmi:settings"); JsonUtils::validate(config, "vcmi:settings", "settings"); @@ -294,4 +294,4 @@ void config::CConfigHandler::init() // Force instantiation of the SettingsStorage::NodeAccessor class template. // That way method definitions can sit in the cpp file template struct SettingsStorage::NodeAccessor; -template struct SettingsStorage::NodeAccessor; \ No newline at end of file +template struct SettingsStorage::NodeAccessor; diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 6d29e72e0..e8db8a5ea 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -547,12 +547,12 @@ std::vector CModHandler::resolveDependencies(std::vector input static JsonNode loadModSettings(std::string path) { - if (CResourceHandler::get()->existsResource(ResourceID(path))) + if (CResourceHandler::get("local")->existsResource(ResourceID(path))) { return JsonNode(ResourceID(path, EResType::TEXT)); } // Probably new install. Create initial configuration - CResourceHandler::get()->createResource(path); + CResourceHandler::get("local")->createResource(path); return JsonNode(); } @@ -687,7 +687,7 @@ static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoade if (modName != "core") { ResourceID modConfFile("mods/" + modName + "/mod", EResType::TEXT); - ui32 configChecksum = CResourceHandler::getInitial()->load(modConfFile)->calculateCRC32(); + ui32 configChecksum = CResourceHandler::get("initial")->load(modConfFile)->calculateCRC32(); modChecksum.process_bytes(reinterpret_cast(&configChecksum), sizeof(configChecksum)); } // third - add all detected text files from this mod into checksum @@ -717,7 +717,7 @@ void CModHandler::loadModFilesystems() for(std::string & modName : activeMods) { CModInfo & mod = allMods[modName]; - CResourceHandler::addFilesystem(modName, genModFilesystem(modName, mod.config)); + CResourceHandler::addFilesystem("data", modName, genModFilesystem(modName, mod.config)); } } diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index 97e91347e..aab8fa02b 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -14,8 +14,6 @@ #include "../VCMIDirs.h" #include "../CStopWatch.h" -CFilesystemList * CResourceHandler::resourceLoader = nullptr; -CFilesystemList * CResourceHandler::initialLoader = nullptr; std::map CResourceHandler::knownLoaders = std::map(); CFilesystemGenerator::CFilesystemGenerator(std::string prefix): @@ -75,7 +73,7 @@ void CFilesystemGenerator::loadDirectory(const std::string &mountPoint, const Js ResourceID resID(URI, EResType::DIRECTORY); - for(auto & loader : CResourceHandler::getInitial()->getResourcesWithName(resID)) + for(auto & loader : CResourceHandler::get("initial")->getResourcesWithName(resID)) { auto filename = loader->getResourceName(resID); filesystem->addLoader(new CFilesystemLoader(mountPoint, *filename, depth), false); @@ -85,7 +83,7 @@ void CFilesystemGenerator::loadDirectory(const std::string &mountPoint, const Js void CFilesystemGenerator::loadZipArchive(const std::string &mountPoint, const JsonNode & config) { std::string URI = prefix + config["path"].String(); - auto filename = CResourceHandler::getInitial()->getResourceName(ResourceID(URI, EResType::ARCHIVE_ZIP)); + auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, EResType::ARCHIVE_ZIP)); if (filename) filesystem->addLoader(new CZipLoader(mountPoint, *filename), false); } @@ -94,7 +92,7 @@ template void CFilesystemGenerator::loadArchive(const std::string &mountPoint, const JsonNode & config) { std::string URI = prefix + config["path"].String(); - auto filename = CResourceHandler::getInitial()->getResourceName(ResourceID(URI, archiveType)); + auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, archiveType)); if (filename) filesystem->addLoader(new CArchiveLoader(mountPoint, *filename), false); } @@ -102,10 +100,10 @@ void CFilesystemGenerator::loadArchive(const std::string &mountPoint, const Json void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const JsonNode & config) { std::string URI = prefix + config["path"].String(); - auto filename = CResourceHandler::getInitial()->getResourceName(ResourceID(URI, EResType::TEXT)); + auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, EResType::TEXT)); if (filename) { - auto configData = CResourceHandler::getInitial()->load(ResourceID(URI, EResType::TEXT))->readAll(); + auto configData = CResourceHandler::get("initial")->load(ResourceID(URI, EResType::TEXT))->readAll(); const JsonNode config((char*)configData.first.get(), configData.second); filesystem->addLoader(new CMappedFileLoader(mountPoint, config), false); } @@ -113,14 +111,17 @@ void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const Json void CResourceHandler::clear() { - delete resourceLoader; - delete initialLoader; + delete knownLoaders["root"]; } -void CResourceHandler::initialize() +ISimpleResourceLoader * CResourceHandler::createInitial() { + //temporary filesystem that will be used to initialize main one. + //used to solve several case-sensivity issues like Mp3 vs MP3 + auto initialLoader = new CFilesystemList; + //recurse only into specific directories - auto recurseInDir = [](std::string URI, int depth) + auto recurseInDir = [&](std::string URI, int depth) { ResourceID ID(URI, EResType::DIRECTORY); @@ -135,10 +136,6 @@ void CResourceHandler::initialize() } }; - //temporary filesystem that will be used to initialize main one. - //used to solve several case-sensivity issues like Mp3 vs MP3 - initialLoader = new CFilesystemList; - for (auto & path : VCMIDirs::get().dataPaths()) { if (boost::filesystem::is_directory(path)) // some of system-provided paths may not exist @@ -148,13 +145,43 @@ void CResourceHandler::initialize() recurseInDir("CONFIG", 0);// look for configs recurseInDir("DATA", 0); // look for archives - //TODO: improve mod loading process so depth 2 will no longer be needed - recurseInDir("MODS", 2); // look for mods. Depth 2 is required for now but won't cause speed issues if no mods present + recurseInDir("MODS", 64); // look for mods. + + return initialLoader; +} + +void CResourceHandler::initialize() +{ + // Create tree-loke structure that looks like this: + // root + // | + // |- initial + // | + // |- data + // | |-core + // | |-mod1 + // | |-modN + // | + // |- local + // |-saves + // |-config + + knownLoaders["root"] = new CFilesystemList(); + knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()); + knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()); + + auto localFS = new CFilesystemList(); + localFS->addLoader(knownLoaders["saves"], true); + localFS->addLoader(knownLoaders["config"], true); + + addFilesystem("root", "initial", createInitial()); + addFilesystem("root", "data", new CFilesystemList()); + addFilesystem("root", "local", localFS); } ISimpleResourceLoader * CResourceHandler::get() { - return get(""); + return get("root"); } ISimpleResourceLoader * CResourceHandler::get(std::string identifier) @@ -162,31 +189,22 @@ ISimpleResourceLoader * CResourceHandler::get(std::string identifier) return knownLoaders.at(identifier); } -ISimpleResourceLoader * CResourceHandler::getInitial() -{ - assert(initialLoader); - return initialLoader; -} - void CResourceHandler::load(const std::string &fsConfigURI) { - auto fsConfigData = initialLoader->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll(); + auto fsConfigData = get("initial")->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll(); const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second); - resourceLoader = new CFilesystemList(); - knownLoaders[""] = resourceLoader; - addFilesystem("core", createFileSystem("", fsConfig["filesystem"])); - - // hardcoded system-specific path, may not be inside any of data directories - resourceLoader->addLoader(new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()), true); - resourceLoader->addLoader(new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()), true); + addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"])); } -void CResourceHandler::addFilesystem(const std::string & identifier, ISimpleResourceLoader * loader) +void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader) { assert(knownLoaders.count(identifier) == 0); - resourceLoader->addLoader(loader, false); + + auto list = dynamic_cast(knownLoaders.at(parent)); + assert(list); + list->addLoader(loader, false); knownLoaders[identifier] = loader; } @@ -201,7 +219,7 @@ std::vector CResourceHandler::getAvailableMods() { static const std::string modDir = "MODS/"; - auto list = initialLoader->getFilteredFiles([](const ResourceID & id) -> bool + auto list = get("initial")->getFilteredFiles([](const ResourceID & id) -> bool { return id.getType() == EResType::DIRECTORY && boost::range::count(id.getName(), '/') == 1 @@ -218,8 +236,8 @@ std::vector CResourceHandler::getAvailableMods() if (name == "WOG") // check if wog is actually present. Hack-ish but better than crash { - if (!initialLoader->existsResource(ResourceID("DATA/ZVS", EResType::DIRECTORY)) && - !initialLoader->existsResource(ResourceID("MODS/WOG/DATA/ZVS", EResType::DIRECTORY))) + if (!get("initial")->existsResource(ResourceID("DATA/ZVS", EResType::DIRECTORY)) && + !get("initial")->existsResource(ResourceID("MODS/WOG/DATA/ZVS", EResType::DIRECTORY))) { continue; } diff --git a/lib/filesystem/Filesystem.h b/lib/filesystem/Filesystem.h index 85e919c2a..d7ec9b35e 100644 --- a/lib/filesystem/Filesystem.h +++ b/lib/filesystem/Filesystem.h @@ -52,6 +52,12 @@ public: */ class DLL_LINKAGE CResourceHandler { + /** + * @brief createInitial - creates instance of initial loader + * that contains data necessary to load main FS + */ + static ISimpleResourceLoader * createInitial(); + public: /** * Gets an instance of resource loader. @@ -62,7 +68,6 @@ public: */ static ISimpleResourceLoader * get(); static ISimpleResourceLoader * get(std::string identifier); - static ISimpleResourceLoader * getInitial(); /** * Creates instance of initial resource loader. @@ -89,7 +94,7 @@ public: * @param identifier name of this loader by which it can be retrieved later * @param loader resource loader to add */ - static void addFilesystem(const std::string & identifier, ISimpleResourceLoader * loader); + static void addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader); /** * @brief createModFileSystem - creates filesystem out of config file @@ -107,6 +112,4 @@ public: private: /** Instance of resource loader */ static std::map knownLoaders; - static CFilesystemList * resourceLoader; - static CFilesystemList * initialLoader; }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 2a6cd78e4..1f643cc6a 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2226,8 +2226,8 @@ void CGameHandler::save(const std::string & filename ) { logGlobal->errorStream() << "Saving to " << filename; CFileInfo info(filename); - //CResourceHandler::get()->createResource(info.getStem() + ".vlgm1"); - CResourceHandler::get()->createResource(info.getStem() + ".vsgm1"); + //CResourceHandler::get("local")->createResource(info.getStem() + ".vlgm1"); + CResourceHandler::get("local")->createResource(info.getStem() + ".vsgm1"); { logGlobal->infoStream() << "Ordering clients to serialize..."; @@ -2239,14 +2239,14 @@ void CGameHandler::save(const std::string & filename ) { // { // logGlobal->infoStream() << "Serializing game info..."; -// CSaveFile save(CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME))); +// CSaveFile save(CResourceHandler::get("local")->getResourceName(ResourceID(info.getStem(), EResType::LIB_SAVEGAME))); // // char hlp[8] = "VCMISVG"; // // save << hlp; // saveCommonState(save); // } { - CSaveFile save(*CResourceHandler::get()->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME))); + CSaveFile save(*CResourceHandler::get("local")->getResourceName(ResourceID(info.getStem(), EResType::SERVER_SAVEGAME))); saveCommonState(save); logGlobal->infoStream() << "Saving server state"; save << *this; diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index fe51e362a..a158209e1 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -464,7 +464,7 @@ void CVCMIServer::loadGame() // CMapHeader dum; // StartInfo *si; // -// CLoadFile lf(CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME))); +// CLoadFile lf(CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME))); // lf >> sig >> dum >> si; // logNetwork->infoStream() <<"Reading save signature"; // @@ -477,7 +477,7 @@ void CVCMIServer::loadGame() // } { - CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)), minSupportedVersion); + CLoadFile lf(*CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::SERVER_SAVEGAME)), minSupportedVersion); gh.loadCommonState(lf); lf >> gh; } diff --git a/vcmibuilder b/vcmibuilder index d2f69dd9c..511b22394 100755 --- a/vcmibuilder +++ b/vcmibuilder @@ -273,7 +273,7 @@ then OIFS="$IFS" IFS=$'\n' - for file in `find "$dest_dir" -type f -name "*.mp3"` + for file in `find "$dest_dir" -type f -iname "*.mp3"` do echo "Converting $file" ogg=${file%.*}