Browse Source

minor refactoring of VCMIDirs, bugfixing

- VCMIDirs represent XDG specification more closely (partial #1310)
- Minor bugfixing, including #1327 #1328 and #1306
Ivan Savenko 12 years ago
parent
commit
4ac2a6e8b6

+ 7 - 8
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 "<<pomtime.getDiff();
 
@@ -324,8 +324,8 @@ int main(int argc, char** argv)
 	{
         logGlobal->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));

+ 1 - 1
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<std::time_t, int> dates; //save number => datestamp
 
 	directory_iterator enddir;

+ 1 - 1
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)

+ 9 - 8
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 <SDL_ttf.h>
@@ -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)};

+ 1 - 1
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"},

+ 2 - 2
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 }
 			]
 
 		},

+ 36 - 15
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<std::string> VCMIDirs::configPaths() const
+{
+	return std::vector<std::string>(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<std::string> VCMIDirs::dataPaths() const
 {
-	return ".";
+	return std::vector<std::string>(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<std::string> VCMIDirs::dataPaths() const
 {
-	return "../Data";
+	return std::vector<std::string>(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<std::string> VCMIDirs::dataPaths() const
 {
-	return M_DATA_DIR;
+	return std::vector<std::string>(1, M_DATA_DIR);
 }
 
 std::string VCMIDirs::libraryName(std::string basename) const

+ 20 - 8
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 where vcmi libraries can be found (in AI and Scripting subdirectories)
-	std::string libraryPath() 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 vcmiserver, including server name (e.g. /usr/bin/vcmiserver)
+	/// Path to config directories, e.g. <data dir>/config. First items have higher priority
+	std::vector<std::string> configPaths() const;
+
+	/// Paths to global system-wide data directories. First items have higher priority
+	std::vector<std::string> dataPaths() const;
+
+	/// Full path to vcmiserver executable, including server name (e.g. /usr/bin/vcmiserver)
 	std::string serverPath() const;
 
-	/// Path to global system-wide data directory
-	std::string dataPath() const;
+	/// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
+	std::string libraryPath() 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;
 };

+ 13 - 12
lib/filesystem/CResourceLoader.cpp

@@ -332,25 +332,26 @@ void CResourceHandler::initialize()
 	initialLoader = new CResourceLoader;
 	resourceLoader = new CResourceLoader;
 
-	shared_ptr<ISimpleResourceLoader> rootDir(new CFilesystemLoader(VCMIDirs::get().dataPath(), 0, true));
-	initialLoader->addLoader("GLOBAL/", rootDir, false);
-	initialLoader->addLoader("ALL/", rootDir, false);
+	for (auto path : VCMIDirs::get().dataPaths())
+	{
+		shared_ptr<ISimpleResourceLoader> loader(new CFilesystemLoader(path, 0, true));
 
-	auto userDir = rootDir;
+		initialLoader->addLoader("GLOBAL/", loader, false);
+		initialLoader->addLoader("ALL/", loader, false);
+	}
 
-	//add local directory to "ALL" but only if it differs from root dir (true for linux)
-	if (VCMIDirs::get().dataPath() != VCMIDirs::get().localPath())
 	{
-		userDir = shared_ptr<ISimpleResourceLoader>(new CFilesystemLoader(VCMIDirs::get().localPath(), 0, true));
-		initialLoader->addLoader("ALL/", userDir, false);
-	}
+		shared_ptr<ISimpleResourceLoader> loader(new CFilesystemLoader(VCMIDirs::get().userDataPath(), 0, true));
 
-	//create "LOCAL" dir with current userDir (may be same as rootDir)
-	initialLoader->addLoader("LOCAL/", userDir, false);
+		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)

+ 3 - 3
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()))
 		{

+ 11 - 2
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);
 				}

+ 1 - 1
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);

+ 1 - 1
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();