From 4ac2a6e8b61ea0606c1500108228d52419814b46 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 8 Jul 2013 20:55:22 +0000 Subject: [PATCH] minor refactoring of VCMIDirs, bugfixing - VCMIDirs represent XDG specification more closely (partial #1310) - Minor bugfixing, including #1327 #1328 and #1306 --- client/CMT.cpp | 15 ++++----- client/CPlayerInterface.cpp | 2 +- client/Client.cpp | 2 +- client/Graphics.cpp | 17 +++++----- config/factions/rampart.json | 2 +- config/mainmenu.json | 4 +-- lib/VCMIDirs.cpp | 51 +++++++++++++++++++++--------- lib/VCMIDirs.h | 30 ++++++++++++------ lib/filesystem/CResourceLoader.cpp | 27 ++++++++-------- scripting/erm/ERMInterpreter.cpp | 6 ++-- server/CGameHandler.cpp | 13 ++++++-- server/CVCMIServer.cpp | 2 +- test/CVcmiTestConfig.cpp | 2 +- 13 files changed, 108 insertions(+), 65 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index f045446a4..a145dac87 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -170,7 +170,7 @@ void init() static void prog_version(void) { printf("%s\n", GameConstants::VCMI_VERSION.c_str()); - printf(" data directory: %s\n", VCMIDirs::get().dataPath().c_str()); + printf(" data directory: %s\n", VCMIDirs::get().dataPaths().back().c_str()); printf(" library directory: %s\n", VCMIDirs::get().libraryPath().c_str()); printf(" path to server: %s\n", VCMIDirs::get().serverPath().c_str()); } @@ -279,7 +279,7 @@ int main(int argc, char** argv) console->start(); atexit(dispose); - CBasicLogConfigurator logConfig(VCMIDirs::get().localPath() + "/VCMI_Client_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Client_log.txt", console); logConfig.configureDefault(); logGlobal->infoStream() <<"Creating console "<errorStream() << "Fatal error: failed to load settings!"; logGlobal->errorStream() << "Possible reasons:"; - logGlobal->errorStream() << "\tCorrupted local configuration file at " << VCMIDirs::get().localPath() << "/config/settings.json"; - logGlobal->errorStream() << "\tMissing or corrupted global configuration file at " << VCMIDirs::get().dataPath() << "/config/schemas/settings.json"; + logGlobal->errorStream() << "\tCorrupted local configuration file at " << VCMIDirs::get().userConfigPath() << "/settings.json"; + logGlobal->errorStream() << "\tMissing or corrupted global configuration file at " << VCMIDirs::get().userConfigPath() << "/schemas/settings.json"; logGlobal->errorStream() << "VCMI will now exit..."; exit(EXIT_FAILURE); } @@ -534,7 +534,7 @@ void processCommand(const std::string &message) { std::cout<<"Command accepted.\t"; - std::string outPath = VCMIDirs::get().localPath() + "/extracted/"; + std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; auto iterator = CResourceHandler::get()->getIterator([](const ResourceID & ident) { @@ -662,8 +662,7 @@ void processCommand(const std::string &message) CDefEssential * cde = CDefHandler::giveDefEss(URI); std::string outName = CResourceHandler::get()->getResource(ResourceID("SPRITES/" + URI)).getResourceName(); - std::string outPath = VCMIDirs::get().localPath() + "/extracted/"; - + std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; boost::filesystem::create_directories(outPath + outName); @@ -684,7 +683,7 @@ void processCommand(const std::string &message) if (CResourceHandler::get()->existsResource(ResourceID(URI))) { std::string outName = CResourceHandler::get()->getResource(ResourceID(URI)).getResourceName(); - std::string outPath = VCMIDirs::get().localPath() + "/extracted/"; + std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/"; std::string fullPath = outPath + outName; auto data = CResourceHandler::get()->loadData(ResourceID(URI)); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index c803bac0a..8c8e8b6dd 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1599,7 +1599,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix) using namespace boost::filesystem; using namespace boost::algorithm; - path gamesDir = VCMIDirs::get().localPath() + "/Games"; + path gamesDir = VCMIDirs::get().userSavePath(); std::map dates; //save number => datestamp directory_iterator enddir; diff --git a/client/Client.cpp b/client/Client.cpp index 8f33bc029..6066c3341 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -826,7 +826,7 @@ CServerHandler::~CServerHandler() void CServerHandler::callServer() { setThreadName("CServerHandler::callServer"); - std::string logName = VCMIDirs::get().localPath() + "/server_log.txt"; + std::string logName = VCMIDirs::get().userCachePath() + "/server_log.txt"; std::string comm = VCMIDirs::get().serverPath() + " --port=" + port + " > " + logName; int result = std::system(comm.c_str()); if (result == 0) diff --git a/client/Graphics.cpp b/client/Graphics.cpp index f4df3dafb..c63dcd506 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -2,6 +2,7 @@ #include "Graphics.h" #include "../lib/filesystem/CResourceLoader.h" +#include "../lib/filesystem/CBinaryReader.h" #include "CDefHandler.h" #include "gui/SDL_Extensions.h" #include @@ -65,18 +66,18 @@ void Graphics::loadPaletteAndColors() } neutralColorPalette = new SDL_Color[32]; - std::ifstream ncp; - ncp.open(CResourceHandler::get()->getResourceName(ResourceID("config/NEUTRAL.PAL")), std::ios::binary); + + auto stream = CResourceHandler::get()->load(ResourceID("config/NEUTRAL.PAL")); + CBinaryReader reader(stream.get()); + for(int i=0; i<32; ++i) { - ncp.read((char*)&neutralColorPalette[i].r,1); - ncp.read((char*)&neutralColorPalette[i].g,1); - ncp.read((char*)&neutralColorPalette[i].b,1); - ncp.read((char*)&neutralColorPalette[i].unused,1); + neutralColorPalette[i].r = reader.readUInt8(); + neutralColorPalette[i].g = reader.readUInt8(); + neutralColorPalette[i].b = reader.readUInt8(); + neutralColorPalette[i].unused = reader.readUInt8(); neutralColorPalette[i].unused = !neutralColorPalette[i].unused; } - - //colors initialization int3 kolory[] = {int3(0xff,0,0),int3(0x31,0x52,0xff),int3(0x9c,0x73,0x52),int3(0x42,0x94,0x29), int3(0xff,0x84,0x0),int3(0x8c,0x29,0xa5),int3(0x09,0x9c,0xa5),int3(0xc6,0x7b,0x8c)}; diff --git a/config/factions/rampart.json b/config/factions/rampart.json index 9a56cea6c..6ee0c7160 100644 --- a/config/factions/rampart.json +++ b/config/factions/rampart.json @@ -171,7 +171,7 @@ "horde1": { "id" : 18, "upgrades" : 31 }, "horde1Upgr": { "id" : 19, "upgrades" : 38, "requires" : [ 18 ], "mode" : "auto" }, "special2": { "id" : 21, "requires" : [ 17 ] }, - "special3": { "id" : 22, "requires" : [ 18, 19 ] }, + "special3": { "id" : 22, "requires" : [ 18 ] }, "horde2": { "id" : 24, "upgrades" : 34 }, "horde2Upgr": { "id" : 25, "upgrades" : 41, "requires" : [ 24 ], "mode" : "auto" }, "grail": { "id" : 26, "mode" : "grail"}, diff --git a/config/mainmenu.json b/config/mainmenu.json index 230cf27af..c6c3eafd2 100644 --- a/config/mainmenu.json +++ b/config/mainmenu.json @@ -76,7 +76,7 @@ { "x":313, "y":244, "file":"DATA/NEUTRAL1.H3C", "image":"CAMPNEUS", "video":"CNEUTRAL", "open": true }, { "x":586, "y":246, "file":"DATA/EVIL2.H3C", "image":"CAMPEV2S", "video":"CEVIL2", "open": true }, { "x":34, "y":417, "file":"DATA/GOOD3.H3C", "image":"CAMPGD3S", "video":"CGOOD3", "open": true }, - { "x":404, "y":414, "file":"DATA/SECRET.H3C", "image":"CAMPSCTS", "video":"CSECRET", "open": true } + { "x":404, "y":414, "file":"DATA/SECRET1.H3C", "image":"CAMPSCTS", "video":"CSECRET", "open": true } ] }, { @@ -111,7 +111,7 @@ { "x":313, "y":244, "file":"DATA/SANDRO.H3C", "image":"CAMPRN1", "video":"RISE", "open": true }, { "x":586, "y":246, "file":"DATA/YOG.H3C", "image":"CAMPBB1", "video":"BIRTH", "open": true }, { "x":34, "y":417, "file":"DATA/FINAL.H3C", "image":"CAMPUA1", "video":"UNHOLY", "open": true }, - { "x":404, "y":414, "file":"DATA/SECRET1.H3C", "image":"CAMPSP1", "video":"SPECTRE", "open": true } + { "x":404, "y":414, "file":"DATA/SECRET.H3C", "image":"CAMPSP1", "video":"SPECTRE", "open": true } ] }, diff --git a/lib/VCMIDirs.cpp b/lib/VCMIDirs.cpp index bb41b2156..645527cf4 100644 --- a/lib/VCMIDirs.cpp +++ b/lib/VCMIDirs.cpp @@ -16,9 +16,10 @@ static VCMIDirs VCMIDirsGlobal; VCMIDirs::VCMIDirs() { // initialize local directory and create folders to which VCMI needs write access - boost::filesystem::create_directory(localPath()); - boost::filesystem::create_directory(localPath() + "/config"); - boost::filesystem::create_directory(localPath() + "/Games"); + boost::filesystem::create_directory(userDataPath()); + boost::filesystem::create_directory(userCachePath()); + boost::filesystem::create_directory(userConfigPath()); + boost::filesystem::create_directory(userSavePath()); } VCMIDirs & VCMIDirs::get() @@ -26,27 +27,47 @@ VCMIDirs & VCMIDirs::get() return VCMIDirsGlobal; } +std::string VCMIDirs::userCachePath() const +{ + return userDataPath(); +} + +std::string VCMIDirs::userConfigPath() const +{ + return userDataPath() + "/config"; +} + +std::string VCMIDirs::userSavePath() const +{ + return userDataPath() + "/Games"; +} + +std::vector VCMIDirs::configPaths() const +{ + return std::vector(1, dataPaths()[0] + "/config"); +} + //FIXME: find way to at least decrease size of this ifdef (along with cleanup in CMake) #if defined(_WIN32) -std::string VCMIDirs::localPath() const +std::string VCMIDirs::userDataPath() const { - return dataPath(); + return dataPaths()[0]; } std::string VCMIDirs::libraryPath() const { - return dataPath(); + return userDataPath(); } std::string VCMIDirs::serverPath() const { - return dataPath() + "\\" + "VCMI_server.exe"; + return userDataPath() + "\\" + "VCMI_server.exe"; } -std::string VCMIDirs::dataPath() const +std::vector VCMIDirs::dataPaths() const { - return "."; + return std::vector(1, "."); } std::string VCMIDirs::libraryName(std::string basename) const @@ -56,7 +77,7 @@ std::string VCMIDirs::libraryName(std::string basename) const #elif defined(__APPLE__) -std::string VCMIDirs::localPath() const +std::string VCMIDirs::userDataPath() const { // This is Cocoa code that should be normally used to get path to Application Support folder but can't use it here for now... // NSArray* urls = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask]; @@ -80,9 +101,9 @@ std::string VCMIDirs::serverPath() const return "./vcmiserver"; } -std::string VCMIDirs::dataPath() const +std::vector VCMIDirs::dataPaths() const { - return "../Data"; + return std::vector(1, "../Data"); } std::string VCMIDirs::libraryName(std::string basename) const @@ -92,7 +113,7 @@ std::string VCMIDirs::libraryName(std::string basename) const #else -std::string VCMIDirs::localPath() const +std::string VCMIDirs::userDataPath() const { if (getenv("HOME") != nullptr ) return std::string(getenv("HOME")) + "/.vcmi"; @@ -109,9 +130,9 @@ std::string VCMIDirs::serverPath() const return std::string(M_BIN_DIR) + "/" + "vcmiserver"; } -std::string VCMIDirs::dataPath() const +std::vector VCMIDirs::dataPaths() const { - return M_DATA_DIR; + return std::vector(1, M_DATA_DIR); } std::string VCMIDirs::libraryName(std::string basename) const diff --git a/lib/VCMIDirs.h b/lib/VCMIDirs.h index 2afdd23f2..7fd756a06 100644 --- a/lib/VCMIDirs.h +++ b/lib/VCMIDirs.h @@ -21,18 +21,30 @@ public: /// get singleton instance static VCMIDirs & get(); - /// Path to local, user-specific directory (e.g. ~/.vcmi on *nix systems) - std::string localPath() const; + /// Path to user-specific data directory + std::string userDataPath() const; + + /// Path to "cache" directory, can be used for any non-essential files + std::string userCachePath() const; + + /// Path to writeable directory with user configs + std::string userConfigPath() const; + + /// Path to saved games + std::string userSavePath() const; + + /// Path to config directories, e.g. /config. First items have higher priority + std::vector configPaths() const; + + /// Paths to global system-wide data directories. First items have higher priority + std::vector dataPaths() const; + + /// Full path to vcmiserver executable, including server name (e.g. /usr/bin/vcmiserver) + std::string serverPath() const; /// Path where vcmi libraries can be found (in AI and Scripting subdirectories) std::string libraryPath() const; - /// Path to vcmiserver, including server name (e.g. /usr/bin/vcmiserver) - std::string serverPath() const; - - /// Path to global system-wide data directory - std::string dataPath() const; - - /// Returns system-specific name for dynamic libraries ("libStupidAI.so" or "StupidAI.dll") + /// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll") std::string libraryName(std::string basename) const; }; diff --git a/lib/filesystem/CResourceLoader.cpp b/lib/filesystem/CResourceLoader.cpp index 1a9be9e19..52e3767ec 100644 --- a/lib/filesystem/CResourceLoader.cpp +++ b/lib/filesystem/CResourceLoader.cpp @@ -332,25 +332,26 @@ void CResourceHandler::initialize() initialLoader = new CResourceLoader; resourceLoader = new CResourceLoader; - shared_ptr rootDir(new CFilesystemLoader(VCMIDirs::get().dataPath(), 0, true)); - initialLoader->addLoader("GLOBAL/", rootDir, false); - initialLoader->addLoader("ALL/", rootDir, false); - - auto userDir = rootDir; - - //add local directory to "ALL" but only if it differs from root dir (true for linux) - if (VCMIDirs::get().dataPath() != VCMIDirs::get().localPath()) + for (auto path : VCMIDirs::get().dataPaths()) { - userDir = shared_ptr(new CFilesystemLoader(VCMIDirs::get().localPath(), 0, true)); - initialLoader->addLoader("ALL/", userDir, false); + shared_ptr loader(new CFilesystemLoader(path, 0, true)); + + initialLoader->addLoader("GLOBAL/", loader, false); + initialLoader->addLoader("ALL/", loader, false); } - //create "LOCAL" dir with current userDir (may be same as rootDir) - initialLoader->addLoader("LOCAL/", userDir, false); + { + shared_ptr loader(new CFilesystemLoader(VCMIDirs::get().userDataPath(), 0, true)); + + initialLoader->addLoader("LOCAL/", loader, false); + + if (!vstd::contains(VCMIDirs::get().dataPaths(), VCMIDirs::get().userDataPath())) + initialLoader->addLoader("ALL/", loader, false); + } recurseInDir("ALL/CONFIG", 0);// look for configs recurseInDir("ALL/DATA", 0); // look for archives - recurseInDir("ALL/MODS", 2); // look for mods. Depth 2 is required for now but won't cause issues if no mods present + recurseInDir("ALL/MODS", 2); // look for mods. Depth 2 is required for now but won't cause spped issues if no mods present } void CResourceHandler::loadDirectory(const std::string &prefix, const std::string &mountPoint, const JsonNode & config) diff --git a/scripting/erm/ERMInterpreter.cpp b/scripting/erm/ERMInterpreter.cpp index a0be39104..bb28712f2 100644 --- a/scripting/erm/ERMInterpreter.cpp +++ b/scripting/erm/ERMInterpreter.cpp @@ -364,13 +364,13 @@ void ERMInterpreter::scanForScripts() { using namespace boost::filesystem; //parser checking - if(!exists(VCMIDirs::get().dataPath() + "/Data/s/")) + if(!exists(VCMIDirs::get().dataPaths().back() + "/Data/s/")) { - logGlobal->warnStream() << "Warning: Folder " << VCMIDirs::get().dataPath() << "/Data/s/ doesn't exist!"; + logGlobal->warnStream() << "Warning: Folder " << VCMIDirs::get().dataPaths().back() << "/Data/s/ doesn't exist!"; return; } directory_iterator enddir; - for (directory_iterator dir(VCMIDirs::get().dataPath() + "/Data/s"); dir!=enddir; dir++) + for (directory_iterator dir(VCMIDirs::get().dataPaths().back() + "/Data/s"); dir!=enddir; dir++) { if(is_regular(dir->status())) { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index aff084276..66cd56ae8 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3765,12 +3765,21 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message } else if (message == "vcmiarmenelos") //build all buildings in selected town { - CGTownInstance *town = gs->getTown(gs->getPlayer(player)->currentSelection); + CGHeroInstance *hero = gs->getHero(gs->getPlayer(player)->currentSelection); + CGTownInstance *town; + + if (hero) + town = hero->visitedTown; + else + town = gs->getTown(gs->getPlayer(player)->currentSelection); + if (town) { for (auto & build : town->town->buildings) { - if (!town->hasBuilt(build.first) && !build.second->Name().empty()) + if (!town->hasBuilt(build.first) + && !build.second->Name().empty() + && build.first != BuildingID::SHIP) { buildStructure(town->id, build.first, true); } diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 55a9273f3..df6a2d261 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -536,7 +536,7 @@ static void handleCommandOptions(int argc, char *argv[]) int main(int argc, char** argv) { console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().localPath() + "/VCMI_Server_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Server_log.txt", console); logConfig.configureDefault(); preinitDLL(console); diff --git a/test/CVcmiTestConfig.cpp b/test/CVcmiTestConfig.cpp index 0dd457d8a..a56c7f380 100644 --- a/test/CVcmiTestConfig.cpp +++ b/test/CVcmiTestConfig.cpp @@ -22,7 +22,7 @@ CVcmiTestConfig::CVcmiTestConfig() { console = new CConsoleHandler; - CBasicLogConfigurator logConfig(VCMIDirs::get().localPath() + "/VCMI_Test_log.txt", console); + CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Test_log.txt", console); logConfig.configureDefault(); preinitDLL(console); settings.init();