Browse Source

Merge branch 'develop' of github.com:karol57/vcmi into karol57-develop

Ivan Savenko 11 years ago
parent
commit
de9c5b1af7
49 changed files with 1126 additions and 853 deletions
  1. 1 1
      AI/BattleAI/main.cpp
  2. 1 1
      AI/StupidAI/main.cpp
  3. 1 1
      AI/VCAI/main.cpp
  4. 2 0
      ChangeLog
  5. 58 22
      Global.h
  6. 40 45
      client/CMT.cpp
  7. 4 13
      client/CPlayerInterface.cpp
  8. 1 1
      client/CVideoHandler.cpp
  9. 1 2
      client/CVideoHandler.h
  10. 10 9
      client/Client.cpp
  11. 1 1
      editor/Editor.cpp
  12. 10 1
      launcher/StdInc.h
  13. 2 2
      launcher/launcherdirs.cpp
  14. 4 4
      launcher/mainwindow_moc.cpp
  15. 1 1
      launcher/modManager/cmodmanager.cpp
  16. 3 3
      launcher/settingsView/csettingsview_moc.cpp
  17. 6 17
      lib/BattleHex.cpp
  18. 15 28
      lib/BattleHex.h
  19. 17 7
      lib/CConsoleHandler.cpp
  20. 2 2
      lib/CConsoleHandler.h
  21. 35 32
      lib/CGameInterface.cpp
  22. 3 3
      lib/CThreadHelper.cpp
  23. 15 17
      lib/CondSh.h
  24. 9 9
      lib/Connection.cpp
  25. 4 4
      lib/Connection.h
  26. 478 153
      lib/VCMIDirs.cpp
  27. 44 38
      lib/VCMIDirs.h
  28. 10 19
      lib/filesystem/CArchiveLoader.cpp
  29. 2 2
      lib/filesystem/CArchiveLoader.h
  30. 15 14
      lib/filesystem/CFileInfo.cpp
  31. 4 6
      lib/filesystem/CFileInputStream.cpp
  32. 3 3
      lib/filesystem/CFileInputStream.h
  33. 56 30
      lib/filesystem/CFilesystemLoader.cpp
  34. 5 5
      lib/filesystem/CFilesystemLoader.h
  35. 2 2
      lib/filesystem/Filesystem.cpp
  36. 8 6
      lib/int3.h
  37. 8 16
      lib/logging/CBasicLogConfigurator.cpp
  38. 9 6
      lib/logging/CBasicLogConfigurator.h
  39. 93 221
      lib/logging/CLogger.cpp
  40. 36 30
      lib/logging/CLogger.h
  41. 1 1
      lib/mapObjects/CArmedInstance.h
  42. 1 1
      lib/mapObjects/CBank.h
  43. 1 1
      lib/mapObjects/CGHeroInstance.h
  44. 1 1
      lib/mapObjects/CGPandoraBox.h
  45. 1 1
      lib/mapObjects/CObjectHandler.h
  46. 87 56
      lib/rmg/float3.h
  47. 6 6
      scripting/erm/ERMInterpreter.cpp
  48. 8 8
      server/CVCMIServer.cpp
  49. 1 1
      test/CVcmiTestConfig.cpp

+ 1 - 1
AI/BattleAI/main.cpp

@@ -7,7 +7,7 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 #define GetGlobalAiVersion BattleAI_GetGlobalAiVersion
 #define GetAiName BattleAI_GetAiName
 #define GetNewBattleAI BattleAI_GetNewBattleAI

+ 1 - 1
AI/StupidAI/main.cpp

@@ -7,7 +7,7 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 #define GetGlobalAiVersion StupidAI_GetGlobalAiVersion
 #define GetAiName StupidAI_GetAiName
 #define GetNewBattleAI StupidAI_GetNewBattleAI

+ 1 - 1
AI/VCAI/main.cpp

@@ -5,7 +5,7 @@
 #define strcpy_s(a, b, c) strncpy(a, c, b)
 #endif
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 #define GetGlobalAiVersion VCAI_GetGlobalAiVersion
 #define GetAiName VCAI_GetAiName
 #define GetNewAI VCAI_GetNewAI

+ 2 - 0
ChangeLog

@@ -3,6 +3,8 @@ GENERAL:
 * VCMI can now be compiled with SDL2
 * Better upscaling when running in fullscreen mode.
 * Non-latin characters can now be entered in chat window or used for save names.
+* (windows) Moved VCMI data directory from '%userprofile%\vcmi' to '%userprofile%\Documents\My Games\vcmi'
+* (windows, OSX) Moved VCMI save directory from 'VCMI_DATA\Games' to 'VCMI_DATA\Saves'
 
 RANDOM MAP GENERATOR:
 

+ 58 - 22
Global.h

@@ -49,10 +49,58 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #  pragma warning (disable : 4800 ) /* disable conversion to bool warning -- I think it's intended in all places */
 #endif
 
+/* ---------------------------------------------------------------------------- */
+/* System detection. */
+/* ---------------------------------------------------------------------------- */
+// Based on: http://sourceforge.net/p/predef/wiki/OperatingSystems/
+//	 and on: http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
+// TODO?: Should be moved to vstd\os_detect.h (and then included by Global.h)
+#ifdef _WIN16			// Defined for 16-bit environments
+#  error "16-bit Windows isn't supported"
+#elif defined(_WIN64)	// Defined for 64-bit environments
+#  define VCMI_WINDOWS
+#  define VCMI_WINDOWS_64
+#elif defined(_WIN32)	// Defined for both 32-bit and 64-bit environments
+#  define VCMI_WINDOWS
+#  define VCMI_WINDOWS_32
+#elif defined(_WIN32_WCE)
+#  error "Windows CE isn't supported"
+#elif defined(__linux__) || defined(__gnu_linux__) || defined(linux) || defined(__linux)
+#  define VCMI_UNIX
+#  define VCMI_LINUX
+#  ifdef __ANDROID__
+#    define VCMI_ANDROID 
+#  endif
+#elif defined(__APPLE__) && defined(__MACH__)
+#  define VCMI_UNIX
+#  define VCMI_APPLE
+#  include "TargetConditionals.h"
+#  if TARGET_IPHONE_SIMULATOR
+#    define VCMI_IOS
+#    define VCMI_IOS_SIM
+#  elif TARGET_OS_IPHONE
+#    define VCMI_IOS
+#  elif TARGET_OS_MAC
+#    define VCMI_MAC
+#  else
+//#  warning "Unknown Apple target."?
+#  endif
+#else
+#  error "VCMI supports only Windows, OSX, Linux and Android targets"
+#endif
+
+#ifdef VCMI_IOS
+#  error "iOS system isn't yet supported."
+#endif
+
 /* ---------------------------------------------------------------------------- */
 /* Commonly used C++, Boost headers */
 /* ---------------------------------------------------------------------------- */
-#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#ifdef VCMI_WINDOWS
+#  define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers - delete this line if something is missing.
+#  define NOMINMAX					// Exclude min/max macros from <Windows.h>. Use std::[min/max] from <algorithm> instead.
+#endif
+
 #define _USE_MATH_DEFINES
 
 #include <cstdio>
@@ -86,7 +134,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 
 #define BOOST_FILESYSTEM_VERSION 3
 #if BOOST_VERSION > 105000
-#define BOOST_THREAD_VERSION 3
+#  define BOOST_THREAD_VERSION 3
 #endif
 #define BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE 1
 #define BOOST_BIND_NO_PLACEHOLDERS
@@ -99,9 +147,12 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/date_time/posix_time/posix_time_io.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/fstream.hpp>
 #include <boost/format.hpp>
 #include <boost/functional/hash.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/locale/generator.hpp>
 #include <boost/logic/tribool.hpp>
 #include <boost/optional.hpp>
 #include <boost/program_options.hpp>
@@ -112,13 +163,8 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
 #include <boost/variant.hpp>
 #include <boost/math/special_functions/round.hpp>
 
-
-#ifdef ANDROID
-#include <android/log.h>
-#endif
-
 #ifndef M_PI
-#define M_PI 3.14159265358979323846
+#  define M_PI 3.14159265358979323846
 #endif
 
 /* ---------------------------------------------------------------------------- */
@@ -151,30 +197,20 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec;
 /* Macros */
 /* ---------------------------------------------------------------------------- */
 // Import + Export macro declarations
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 #  ifdef __GNUC__
+#    define DLL_IMPORT __attribute__((dllimport))
 #    define DLL_EXPORT __attribute__((dllexport))
 #  else
+#    define DLL_IMPORT __declspec(dllimport)
 #    define DLL_EXPORT __declspec(dllexport)
 #  endif
 #  define ELF_VISIBILITY
 #else
 #  ifdef __GNUC__
+#    define DLL_IMPORT	__attribute__ ((visibility("default")))
 #    define DLL_EXPORT __attribute__ ((visibility("default")))
 #    define ELF_VISIBILITY __attribute__ ((visibility("default")))
-#  endif
-#endif
-
-#ifdef _WIN32
-#  ifdef __GNUC__
-#    define DLL_IMPORT __attribute__((dllimport))
-#  else
-#    define DLL_IMPORT __declspec(dllimport)
-#  endif
-#  define ELF_VISIBILITY
-#else
-#  ifdef __GNUC__
-#    define DLL_IMPORT	__attribute__ ((visibility("default")))
 #    define ELF_VISIBILITY __attribute__ ((visibility("default")))
 #  endif
 #endif

+ 40 - 45
client/CMT.cpp

@@ -41,7 +41,7 @@
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/CondSh.h"
 
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 #include "SDL_syswm.h"
 #endif
 #include "../lib/UnlockGuard.h"
@@ -52,6 +52,7 @@
 #endif
 
 namespace po = boost::program_options;
+namespace bfs = boost::filesystem;
 
 /*
  * CMT.cpp, part of VCMI engine
@@ -73,13 +74,12 @@ int preferredDriverIndex = -1;
 SDL_Window * mainWindow = nullptr;
 SDL_Renderer * mainRenderer = nullptr;
 SDL_Texture * screenTexture = nullptr;
-
 #endif // VCMI_SDL1
 
 extern boost::thread_specific_ptr<bool> inGuiThread;
 
 SDL_Surface *screen = nullptr, //main screen surface
-	*screen2 = nullptr,//and hlp surface (used to store not-active interfaces layer)
+	*screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer)
 	*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
 	
 std::queue<SDL_Event> events;
@@ -100,31 +100,29 @@ static void mainLoop();
 void startGame(StartInfo * options, CConnection *serv = nullptr);
 void endGame();
 
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
 #include <getopt.h>
 #endif
 
-void startGameFromFile(const std::string &fname)
+void startGameFromFile(const bfs::path &fname)
 {
 	StartInfo si;
 	try //attempt retrieving start info from given file
 	{
-		if(!fname.size() || !boost::filesystem::exists(fname))
-			throw std::runtime_error("Startfile \"" + fname + "\" does not exist!");
+		if(fname.empty() || !bfs::exists(fname))
+			throw std::runtime_error("Startfile \"" + fname.string() + "\" does not exist!");
 
 		CLoadFile out(fname);
-		if(!out.sfile || !*out.sfile)
-		{
-			throw std::runtime_error("Cannot read from startfile \"" + fname + "\"!");
-		}
+		if (!out.sfile || !*out.sfile)
+			throw std::runtime_error("Cannot read from startfile \"" + fname.string() +"\"!");
 		out >> si;
 	}
 	catch(std::exception &e)
 	{
-		logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what()
+		logGlobal->errorStream() << "Failed to start from the file: " << fname << ". Error: " << e.what()
 			<< " Falling back to main menu.";
 		GH.curInt = CGPreGame::create();
 		return;
@@ -184,19 +182,19 @@ static void prog_help(const po::options_description &opts)
 // 	printf("  -v, --version     display version information and exit\n");
 }
 
-#ifdef __APPLE__
+#ifdef VCMI_APPLE
 void OSX_checkForUpdates();
 #endif
 
-#if defined(_WIN32) && !defined (__GNUC__)
+#if defined(VCMI_WINDOWS) && !defined (__GNUC__)
 int wmain(int argc, wchar_t* argv[])
-#elif defined(__APPLE__)
+#elif defined(VCMI_APPLE)
 int SDL_main(int argc, char *argv[])
 #else
 int main(int argc, char** argv)
 #endif
 {
-#ifdef __APPLE__
+#ifdef VCMI_APPLE
 	// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
     std::string executablePath = argv[0];
     std::string workDir = executablePath.substr(0, executablePath.rfind('/'));
@@ -206,7 +204,7 @@ int main(int argc, char** argv)
     OSX_checkForUpdates();
 
     // Check that game data is prepared. Otherwise run vcmibuilder helper application
-    FILE* check = fopen((VCMIDirs::get().userDataPath() + "/game_data_prepared").c_str(), "r");
+    FILE* check = fopen((VCMIDirs::get().userDataPath() / "game_data_prepared").string().c_str(), "r");
     if (check == nullptr) {
         system("open ./vcmibuilder.app");
         return 0;
@@ -219,7 +217,7 @@ int main(int argc, char** argv)
 		("help,h", "display help and exit")
 		("version,v", "display version information and exit")
 		("battle,b", po::value<std::string>(), "runs game in duel mode (battle-only")
-		("start", po::value<std::string>(), "starts game from saved StartInfo file")
+		("start", po::value<bfs::path>(), "starts game from saved StartInfo file")
 		("onlyAI", "runs without human player, all players will be default AI")
 		("noGUI", "runs without GUI, implies --onlyAI")
 		("ai", po::value<std::vector<std::string>>(), "AI to be used for the player, can be specified several times for the consecutive players")
@@ -272,17 +270,17 @@ int main(int argc, char** argv)
 	CStopWatch total, pomtime;
 	std::cout.flags(std::ios::unitbuf);
 	console = new CConsoleHandler;
-	*console->cb = std::bind(&processCommand, _1);
+	*console->cb = processCommand;
 	console->start();
 	atexit(dispose);
 
-	const auto logPath = VCMIDirs::get().userCachePath() + "/VCMI_Client_log.txt";
+	const bfs::path logPath = VCMIDirs::get().userCachePath() / "VCMI_Client_log.txt";
 	CBasicLogConfigurator logConfig(logPath, console);
     logConfig.configureDefault();
 	logGlobal->infoStream() << "Creating console and configuring logger: " << pomtime.getDiff();
 	logGlobal->infoStream() << "The log file will be saved to " << logPath;
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 	// boost will crash without this
 	setenv("LANG", "C", 1);
 #endif
@@ -388,7 +386,7 @@ int main(int argc, char** argv)
 
     logGlobal->infoStream()<<"\tInitializing video: "<<pomtime.getDiff();
 
-#if defined(__ANDROID__)
+#if defined(VCMI_ANDROID)
 	//on Android threaded init is broken
 	#define VCMI_NO_THREADED_LOAD
 #endif // defined
@@ -430,15 +428,15 @@ int main(int argc, char** argv)
 		session["autoSkip"].Bool()  = vm.count("autoSkip");
 		session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
 
-		std::string fileToStartFrom; //none by default
+		bfs::path fileToStartFrom; //none by default
 		if(vm.count("start"))
-			fileToStartFrom = vm["start"].as<std::string>();
+			fileToStartFrom = vm["start"].as<bfs::path>();
 
-		if(fileToStartFrom.size() && boost::filesystem::exists(fileToStartFrom))
+		if(!fileToStartFrom.empty() && bfs::exists(fileToStartFrom))
 			startGameFromFile(fileToStartFrom); //ommit pregame and start the game using settings from file
 		else
 		{
-			if(fileToStartFrom.size())
+			if(!fileToStartFrom.empty())
 			{
                 logGlobal->warnStream() << "Warning: cannot find given file to start from (" << fileToStartFrom
                     << "). Falling back to main menu.";
@@ -584,7 +582,8 @@ void processCommand(const std::string &message)
 	{
         std::cout<<"Command accepted.\t";
 
-		std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
+		const bfs::path outPath =
+			VCMIDirs::get().userCachePath() / "extracted";
 
 		auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
 		{
@@ -593,18 +592,18 @@ void processCommand(const std::string &message)
 
 		for (auto & filename : list)
 		{
-			std::string outName = outPath + filename.getName();
-
-			boost::filesystem::create_directories(outName.substr(0, outName.find_last_of("/")));
+			const bfs::path filePath = outPath / (filename.getName() + ".TXT");
+			
+			bfs::create_directories(filePath.parent_path());
 
-			std::ofstream file(outName + ".TXT");
+			bfs::ofstream file(filePath);
 			auto text = CResourceHandler::get()->load(filename)->readAll();
 
 			file.write((char*)text.first.get(), text.second);
 		}
 
         std::cout << "\rExtracting done :)\n";
-        std::cout << " Extracted files can be found in " << outPath << " directory\n";
+		std::cout << " Extracted files can be found in " << outPath << " directory\n";
 	}
 	else if(cn=="crash")
 	{
@@ -710,15 +709,13 @@ void processCommand(const std::string &message)
 		{
 			CDefEssential * cde = CDefHandler::giveDefEss(URI);
 
-			std::string outName = URI;
-			std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
-
-			boost::filesystem::create_directories(outPath + outName);
+			const bfs::path outPath = VCMIDirs::get().userCachePath() / "extracted" / URI;
+			bfs::create_directories(outPath);
 
-			for (size_t i=0; i<cde->ourImages.size(); i++)
+			for (size_t i = 0; i < cde->ourImages.size(); ++i)
 			{
-				std::string filename = outPath + outName + '/' + boost::lexical_cast<std::string>(i) + ".bmp";
-				SDL_SaveBMP(cde->ourImages[i].bitmap, filename.c_str());
+				const bfs::path filePath = outPath / (boost::lexical_cast<std::string>(i)+".bmp");
+				SDL_SaveBMP(cde->ourImages[i].bitmap, filePath.string().c_str());
 			}
 		}
 		else
@@ -731,14 +728,12 @@ void processCommand(const std::string &message)
 
 		if (CResourceHandler::get()->existsResource(ResourceID(URI)))
 		{
-			std::string outName = URI;
-			std::string outPath = VCMIDirs::get().userCachePath() + "/extracted/";
-			std::string fullPath = outPath + outName;
+			const bfs::path outPath = VCMIDirs::get().userCachePath() / "extracted" / URI;
 
 			auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
 
-			boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/")));
-			std::ofstream outFile(outPath + outName, std::ofstream::binary);
+			bfs::create_directories(outPath.parent_path());
+			bfs::ofstream outFile(outPath, bfs::ofstream::binary);
 			outFile.write((char*)data.first.get(), data.second);
 		}
 		else
@@ -1013,7 +1008,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 	SDL_ShowCursor(SDL_DISABLE);
 	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
 
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 	SDL_SysWMinfo wm;
 	SDL_VERSION(&wm.version);
 	int getwm = SDL_GetWMInfo(&wm);

+ 4 - 13
client/CPlayerInterface.cpp

@@ -42,13 +42,6 @@
 #include "../lib/UnlockGuard.h"
 #include <SDL.h>
 
-#ifdef min
-#undef min
-#endif
-#ifdef max
-#undef max
-#endif
-
 /*
  * CPlayerInterface.cpp, part of VCMI engine
  *
@@ -1622,22 +1615,20 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
 	path gamesDir = VCMIDirs::get().userSavePath();
 	std::map<std::time_t, int> dates; //save number => datestamp
 
-	directory_iterator enddir;
+	const directory_iterator enddir;
 	if(!exists(gamesDir))
 		create_directory(gamesDir);
-
-	for (directory_iterator dir(gamesDir); dir!=enddir; dir++)
+	else
+	for (directory_iterator dir(gamesDir); dir != enddir; ++dir)
 	{
 		if(is_regular(dir->status()))
 		{
-			std::string name = dir->path().leaf().string();
+			std::string name = dir->path().filename().string();
 			if(starts_with(name, namePrefix) && ends_with(name, ".vcgm1"))
 			{
 				char nr = name[namePrefix.size()];
 				if(std::isdigit(nr))
-				{
 					dates[last_write_time(dir->path())] = boost::lexical_cast<int>(nr);
-				}
 			}
 		}
 	}

+ 1 - 1
client/CVideoHandler.cpp

@@ -23,7 +23,7 @@ static bool keyDown()
 }
 #endif
 
-#if defined(_WIN32)  &&  (_MSC_VER < 1800 ||  !defined(USE_FFMPEG))
+#if defined(VCMI_WINDOWS)  &&  (_MSC_VER < 1800 ||  !defined(USE_FFMPEG))
 
 void checkForError(bool throwing = true)
 {

+ 1 - 2
client/CVideoHandler.h

@@ -43,9 +43,8 @@ public:
 };
 
 
-#if defined(_WIN32)  &&  (_MSC_VER < 1800 ||  !defined(USE_FFMPEG))
+#if defined(VCMI_WINDOWS)  &&  (_MSC_VER < 1800 ||  !defined(USE_FFMPEG))
 
-#define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
 #include <windows.h>
 
 #pragma pack(push,1)

+ 10 - 9
client/Client.cpp

@@ -20,7 +20,7 @@
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CSpellHandler.h"
 #include "../lib/Connection.h"
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 #include "../lib/Interprocess.h"
 #endif
 #include "../lib/NetPacks.h"
@@ -40,7 +40,7 @@
 #include "CMT.h"
 
 extern std::string NAME;
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 namespace intpr = boost::interprocess;
 #endif
 
@@ -780,8 +780,9 @@ std::string CClient::aiNameForPlayer(const PlayerSettings &ps, bool battleAI)
 {
 	if(ps.name.size())
 	{
-		std::string filename = VCMIDirs::get().libraryPath() + "/AI/" + VCMIDirs::get().libraryName(ps.name);
-		if(boost::filesystem::exists(filename))
+		const boost::filesystem::path aiPath =
+			VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(ps.name);
+		if (boost::filesystem::exists(aiPath))
 			return ps.name;
 	}
 
@@ -814,7 +815,7 @@ void CServerHandler::waitForServer()
 		startServer();
 
 	th.update();
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 	intpr::scoped_lock<intpr::interprocess_mutex> slock(shared->sr->mutex);
 	while(!shared->sr->ready)
 	{
@@ -827,7 +828,7 @@ void CServerHandler::waitForServer()
 
 CConnection * CServerHandler::connectToServer()
 {
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 	if(!shared->sr->ready)
 		waitForServer();
 #else
@@ -850,7 +851,7 @@ CServerHandler::CServerHandler(bool runServer /*= false*/)
 	port = boost::lexical_cast<std::string>(settings["server"]["port"].Float());
 	verbose = true;
 
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 	boost::interprocess::shared_memory_object::remove("vcmi_memory"); //if the application has previously crashed, the memory may not have been removed. to avoid problems - try to destroy it
 	try
 	{
@@ -868,8 +869,8 @@ CServerHandler::~CServerHandler()
 void CServerHandler::callServer()
 {
 	setThreadName("CServerHandler::callServer");
-	std::string logName = VCMIDirs::get().userCachePath() + "/server_log.txt";
-	std::string comm = VCMIDirs::get().serverPath() + " --port=" + port + " > " + logName;
+	const std::string logName = (VCMIDirs::get().userCachePath() / "server_log.txt").string();
+	const std::string comm = VCMIDirs::get().serverPath().string() + " --port=" + port + " > \"" + logName + '\"';
 	int result = std::system(comm.c_str());
 	if (result == 0)
         logNetwork->infoStream() << "Server closed correctly";

+ 1 - 1
editor/Editor.cpp

@@ -13,7 +13,7 @@ Editor::Editor(QWidget *parent)
 {
 	// Setup default logging(enough for now)
 	console = new CConsoleHandler;
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Editor_log.txt", console);
+	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Editor_log.txt", console);
 	logConfig.configureDefault();
 
 	preinitDLL(console);

+ 10 - 1
launcher/StdInc.h

@@ -8,4 +8,13 @@
 #include <QVector>
 #include <QList>
 #include <QString>
-#include <QFile>
+#include <QFile>
+
+inline QString pathToQString(const boost::filesystem::path & path)
+{
+#ifdef VCMI_WINDOWS
+	return QString::fromStdWString(path.wstring());
+#else
+	return QString::fromStdString(path.string());
+#endif
+}

+ 2 - 2
launcher/launcherdirs.cpp

@@ -18,10 +18,10 @@ CLauncherDirs & CLauncherDirs::get()
 
 QString CLauncherDirs::downloadsPath()
 {
-	return QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()) + "/downloads";
+	return pathToQString(VCMIDirs::get().userCachePath() / "downloads");
 }
 
 QString CLauncherDirs::modsPath()
 {
-	return QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/Mods";
+	return pathToQString(VCMIDirs::get().userDataPath() / "Mods");
 }

+ 4 - 4
launcher/mainwindow_moc.cpp

@@ -13,15 +13,15 @@
 void MainWindow::load()
 {
 	console = new CConsoleHandler;
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/VCMI_Launcher_log.txt", console);
+	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Launcher_log.txt", console);
 	logConfig.configureDefault();
 
 	CResourceHandler::initialize();
 	CResourceHandler::load("config/filesystem.json");
 
 	for (auto & string : VCMIDirs::get().dataPaths())
-		QDir::addSearchPath("icons", QString::fromUtf8(string.c_str()) + "/launcher/icons");
-	QDir::addSearchPath("icons", QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/launcher/icons");
+		QDir::addSearchPath("icons", pathToQString(string / "launcher" / "icons"));
+	QDir::addSearchPath("icons", pathToQString(VCMIDirs::get().userDataPath() / "launcher" / "icons"));
 
 	settings.init();
 }
@@ -46,7 +46,7 @@ MainWindow::~MainWindow()
 
 void MainWindow::on_startGameButon_clicked()
 {
-	startExecutable(QString::fromUtf8(VCMIDirs::get().clientPath().c_str()));
+	startExecutable(pathToQString(VCMIDirs::get().clientPath()));
 }
 
 void MainWindow::startExecutable(QString name)

+ 1 - 1
launcher/modManager/cmodmanager.cpp

@@ -39,7 +39,7 @@ CModManager::CModManager(CModList * modList):
 
 QString CModManager::settingsPath()
 {
-	return QString::fromUtf8(VCMIDirs::get().userConfigPath().c_str()) + "/modSettings.json";
+	return pathToQString(VCMIDirs::get().userConfigPath() / "modSettings.json");
 }
 
 void CModManager::loadModSettings()

+ 3 - 3
launcher/settingsView/csettingsview_moc.cpp

@@ -46,9 +46,9 @@ void CSettingsView::loadSettings()
 	for (auto entry : urls.Vector())
 		ui->plainTextEditRepos->appendPlainText(QString::fromUtf8(entry.String().c_str()));
 
-	ui->lineEditUserDataDir->setText(QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()));
-	ui->lineEditGameDir->setText(QString::fromUtf8(M_DATA_DIR));
-	ui->lineEditTempDir->setText(QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()));
+	ui->lineEditUserDataDir->setText(pathToQString(VCMIDirs::get().userDataPath()));
+	ui->lineEditGameDir->setText(pathToQString(VCMIDirs::get().binaryPath()));
+	ui->lineEditTempDir->setText(pathToQString(VCMIDirs::get().userCachePath()));
 
 	std::string encoding = settings["general"]["encoding"].String();
 	size_t encodingIndex = boost::range::find(knownEncodingsList, encoding) - knownEncodingsList;

+ 6 - 17
lib/BattleHex.cpp

@@ -44,18 +44,6 @@ BattleHex& BattleHex::moveInDir(EDir dir, bool hasToBeValid)
 	return *this;
 }
 
-void BattleHex::operator+=(EDir dir)
-{
-	moveInDir(dir);
-}
-
-BattleHex BattleHex::operator+(EDir dir) const
-{
-	BattleHex ret(*this);
-	ret += dir;
-	return ret;
-}
-
 std::vector<BattleHex> BattleHex::neighbouringTiles() const
 {
 	std::vector<BattleHex> ret;
@@ -93,17 +81,18 @@ char BattleHex::getDistance(BattleHex hex1, BattleHex hex2)
 {	
 	int y1 = hex1.getY(), 
 		y2 = hex2.getY();
-
-	int x1 = hex1.getX() + y1 / 2.0, 
-		x2 = hex2.getX() + y2 / 2.0;
+	
+	// FIXME: Omit floating point arithmetics
+	int x1 = (int)(hex1.getX() + y1 * 0.5),
+		x2 = (int)(hex2.getX() + y2 * 0.5);
 
 	int xDst = x2 - x1,
 		yDst = y2 - y1;
 
 	if ((xDst >= 0 && yDst >= 0) || (xDst < 0 && yDst < 0)) 
 		return std::max(std::abs(xDst), std::abs(yDst));
-	else 
-		return std::abs(xDst) + std::abs(yDst);
+	
+	return std::abs(xDst) + std::abs(yDst);
 }
 
 void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)

+ 15 - 28
lib/BattleHex.h

@@ -17,22 +17,16 @@
 struct DLL_LINKAGE BattleHex
 {
 	static const si16 INVALID = -1;
-	enum EDir{RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT};
+	enum EDir { RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT, TOP_LEFT, TOP_RIGHT };
 
 	si16 hex;
 
 	BattleHex() : hex(INVALID) {}
 	BattleHex(si16 _hex) : hex(_hex) {}
 	
-	operator si16() const
-	{
-		return hex;
-	}
+	operator si16() const { return hex; }
 
-	bool isValid() const
-	{
-		return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
-	}
+	bool isValid() const { return hex >= 0 && hex < GameConstants::BFIELD_SIZE; }
 
 	template<typename inttype>
 	BattleHex(inttype x, inttype y)
@@ -61,9 +55,7 @@ struct DLL_LINKAGE BattleHex
 	void setXY(si16 x, si16 y, bool hasToBeValid = true)
 	{
 		if(hasToBeValid)
-		{
-			assert(x >= 0 && x < GameConstants::BFIELD_WIDTH && y >= 0  && y < GameConstants::BFIELD_HEIGHT);
-		}
+			assert(x >= 0 && x < GameConstants::BFIELD_WIDTH && y >= 0 && y < GameConstants::BFIELD_HEIGHT);
 		hex = x + y * GameConstants::BFIELD_WIDTH;
 	}
 
@@ -73,28 +65,23 @@ struct DLL_LINKAGE BattleHex
 		setXY(xy.first, xy.second);
 	}
 
-	si16 getY() const
-	{
-		return hex / GameConstants::BFIELD_WIDTH;
-	}
+	si16 getY() const { return hex / GameConstants::BFIELD_WIDTH; }
+	si16 getX() const { return hex % GameConstants::BFIELD_WIDTH; }
 
-	si16 getX() const
-	{
-		int pos = hex - getY() * GameConstants::BFIELD_WIDTH;
-		return pos;
-	}
-
-	std::pair<si16, si16> getXY() const
-	{
-		return std::make_pair(getX(), getY());
-	}
+	std::pair<si16, si16> getXY() const { return std::make_pair(getX(), getY()); }
 
 	//moving to direction
 	BattleHex& moveInDir(EDir dir, bool hasToBeValid = true); 
-	void operator+=(EDir dir); //sugar for above
+	BattleHex& operator+=(EDir dir) { return moveInDir(dir); } //sugar for above
 
 	//generates new BattleHex moved by given dir
-	BattleHex operator+(EDir dir) const;
+	BattleHex movedInDir(EDir dir, bool hasToBeValid = true) const
+	{
+		BattleHex result(*this);
+		result.moveInDir(dir, hasToBeValid);
+		return result;
+	}
+	BattleHex operator+(EDir dir) const { return movedInDir(dir); }
 
 	std::vector<BattleHex> neighbouringTiles() const;
 

+ 17 - 7
lib/CConsoleHandler.cpp

@@ -17,7 +17,7 @@ boost::mutex CConsoleHandler::smx;
 
 DLL_LINKAGE CConsoleHandler * console = nullptr;
 
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 	typedef std::string TColor;
 	#define CONSOLE_GREEN "\x1b[1;32m"
 	#define CONSOLE_RED "\x1b[1;31m"
@@ -27,7 +27,6 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
 	#define CONSOLE_GRAY "\x1b[1;30m"
 	#define CONSOLE_TEAL "\x1b[1;36m"
 #else
-	#define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
 	#include <Windows.h>
 #ifndef __MINGW32__
 	#include <dbghelp.h>
@@ -36,6 +35,7 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
 	typedef WORD TColor;
 	HANDLE handleIn;
 	HANDLE handleOut;
+	HANDLE handleErr;
 	#define CONSOLE_GREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY
 	#define CONSOLE_RED FOREGROUND_RED | FOREGROUND_INTENSITY
 	#define CONSOLE_MAGENTA FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY
@@ -43,11 +43,13 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
 	#define CONSOLE_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
 	#define CONSOLE_GRAY FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
 	#define CONSOLE_TEAL FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
+
+	static TColor defErrColor;
 #endif
 
 static TColor defColor;
 
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 
 void printWinError()
 {
@@ -178,8 +180,11 @@ void CConsoleHandler::setColor(EConsoleTextColor::EConsoleTextColor color)
         colorCode = defColor;
 		break;
 	}
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
     SetConsoleTextAttribute(handleOut, colorCode);
+	if (color == EConsoleTextColor::DEFAULT)
+		colorCode = defErrColor;
+	SetConsoleTextAttribute(handleErr, colorCode);
 #else
     std::cout << colorCode;
 #endif
@@ -194,7 +199,7 @@ int CConsoleHandler::run()
 
 	while ( std::cin.good() )
 	{
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 		//check if we have some unreaded symbols
 		if (std::cin.rdbuf()->in_avail())
 		{
@@ -216,12 +221,17 @@ int CConsoleHandler::run()
 }
 CConsoleHandler::CConsoleHandler() : thread(nullptr)
 {
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 	handleIn = GetStdHandle(STD_INPUT_HANDLE);
 	handleOut = GetStdHandle(STD_OUTPUT_HANDLE);
+	handleErr = GetStdHandle(STD_ERROR_HANDLE);
+
 	CONSOLE_SCREEN_BUFFER_INFO csbi;
 	GetConsoleScreenBufferInfo(handleOut,&csbi);
 	defColor = csbi.wAttributes;
+
+	GetConsoleScreenBufferInfo(handleErr, &csbi);
+	defErrColor = csbi.wAttributes;
 #ifndef _DEBUG
 	SetUnhandledExceptionFilter(onUnhandledException);
 #endif
@@ -241,7 +251,7 @@ void CConsoleHandler::end()
 {
 	if (thread)
 	{
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 		thread->interrupt();
 #else
 		TerminateThread(thread->native_handle(),0);

+ 2 - 2
lib/CConsoleHandler.h

@@ -38,7 +38,7 @@ public:
     template<typename T> void print(const T &data, bool addNewLine = false, EConsoleTextColor::EConsoleTextColor color = EConsoleTextColor::DEFAULT, bool printToStdErr = false)
 	{
         TLockGuard _(smx);
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 		// with love from ffmpeg - library is trying to print some warnings from separate thread
 		// this results in broken console on Linux. Lock stdout to print all our data at once
 		flockfile(stdout);
@@ -70,7 +70,7 @@ public:
         }
 
         if(color != EConsoleTextColor::DEFAULT) setColor(EConsoleTextColor::DEFAULT);
-#ifndef _WIN32
+#ifndef VCMI_WINDOWS
 		funlockfile(stdout);
 #endif
 	}

+ 35 - 32
lib/CGameInterface.cpp

@@ -4,8 +4,7 @@
 #include "BattleState.h"
 #include "VCMIDirs.h"
 
-#ifdef _WIN32
-	#define WIN32_LEAN_AND_MEAN //excludes rarely used stuff from windows headers - delete this line if something is missing
+#ifdef VCMI_WINDOWS
 	#include <windows.h> //for .dll libs
 #else
 	#include <dlfcn.h>
@@ -22,7 +21,7 @@
  *
  */
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 // we can't use shared libraries on Android so here's a hack
 extern "C" DLL_EXPORT void VCAI_GetAiName(char* name);
 extern "C" DLL_EXPORT void VCAI_GetNewAI(shared_ptr<CGlobalAI> &out);
@@ -35,7 +34,7 @@ extern "C" DLL_EXPORT void BattleAI_GetNewBattleAI(shared_ptr<CBattleGameInterfa
 #endif
 
 template<typename rett>
-shared_ptr<rett> createAny(std::string dllname, std::string methodName)
+shared_ptr<rett> createAny(const boost::filesystem::path& libpath, const std::string& methodName)
 {
 	typedef void(*TGetAIFun)(shared_ptr<rett>&); 
 	typedef void(*TGetNameFun)(char*); 
@@ -45,56 +44,60 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 	TGetAIFun getAI = nullptr;
 	TGetNameFun getName = nullptr;
 
-#ifdef __ANDROID__
+#ifdef VCMI_ANDROID
 	// this is awful but it seems using shared libraries on some devices is even worse
-	if (dllname.find("libVCAI.so") != std::string::npos) {
+	const std::string filename = libpath.filename().string();
+	if (filename == "libVCAI.so")
+	{
 		getName = (TGetNameFun)VCAI_GetAiName;
 		getAI = (TGetAIFun)VCAI_GetNewAI;
-	} else if (dllname.find("libStupidAI.so") != std::string::npos) {
+	}
+	else if (filename == "libStupidAI.so")
+	{
 		getName = (TGetNameFun)StupidAI_GetAiName;
 		getAI = (TGetAIFun)StupidAI_GetNewBattleAI;
-	} else if (dllname.find("libBattleAI.so") != std::string::npos) {
+	}
+	else if (filename == "libBattleAI.so")
+	{
 		getName = (TGetNameFun)BattleAI_GetAiName;
 		getAI = (TGetAIFun)BattleAI_GetNewBattleAI;
-	} else {
-		throw std::runtime_error("Don't know what to do with " + dllname + " and method " + methodName);
 	}
-#else
-
-#ifdef _WIN32
-	HINSTANCE dll = LoadLibraryA(dllname.c_str());
+	else
+		throw std::runtime_error("Don't know what to do with " + libpath.string() + " and method " + methodName);
+#else // !VCMI_ANDROID
+#ifdef VCMI_WINDOWS
+	HMODULE dll = LoadLibraryW(libpath.c_str());
 	if (dll)
 	{
-		getName = (TGetNameFun)GetProcAddress(dll,"GetAiName");
-		getAI = (TGetAIFun)GetProcAddress(dll,methodName.c_str());
+		getName = (TGetNameFun)GetProcAddress(dll, "GetAiName");
+		getAI = (TGetAIFun)GetProcAddress(dll, methodName.c_str());
 	}
-#else
-	void *dll = dlopen(dllname.c_str(), RTLD_LOCAL | RTLD_LAZY);
+#else // !VCMI_WINDOWS
+	void *dll = dlopen(libpath.string().c_str(), RTLD_LOCAL | RTLD_LAZY);
 	if (dll)
 	{
-		getName = (TGetNameFun)dlsym(dll,"GetAiName");
-		getAI = (TGetAIFun)dlsym(dll,methodName.c_str());
+		getName = (TGetNameFun)dlsym(dll, "GetAiName");
+		getAI = (TGetAIFun)dlsym(dll, methodName.c_str());
 	}
 	else
         logGlobal->errorStream() << "Error: " << dlerror();
-#endif
+#endif // VCMI_WINDOWS
 	if (!dll)
 	{
-        logGlobal->errorStream() << "Cannot open dynamic library ("<<dllname<<"). Throwing...";
+		logGlobal->errorStream() << "Cannot open dynamic library ("<<libpath<<"). Throwing...";
 		throw std::runtime_error("Cannot open dynamic library");
 	}
 	else if(!getName || !getAI)
 	{
-        logGlobal->errorStream() << dllname << " does not export method " << methodName;
-#ifdef _WIN32
+		logGlobal->errorStream() << libpath << " does not export method " << methodName;
+#ifdef VCMI_WINDOWS
 		FreeLibrary(dll);
 #else
 		dlclose(dll);
 #endif
 		throw std::runtime_error("Cannot find method " + methodName);
 	}
-
-#endif // __ANDROID__
+#endif // VCMI_ANDROID
 
 	getName(temp);
     logGlobal->infoStream() << "Loaded " << temp;
@@ -108,13 +111,13 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName)
 }
 
 template<typename rett>
-shared_ptr<rett> createAnyAI(std::string dllname, std::string methodName)
+shared_ptr<rett> createAnyAI(std::string dllname, const std::string& methodName)
 {
-    logGlobal->infoStream() << "Opening " << dllname;
-	std::string filename = VCMIDirs::get().libraryName(dllname);
-
-	auto ret = createAny<rett>(VCMIDirs::get().libraryPath() + "/AI/" + filename, methodName);
-	ret->dllName = dllname;
+	logGlobal->infoStream() << "Opening " << dllname;
+	const boost::filesystem::path filePath =
+		VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname);
+	auto ret = createAny<rett>(filePath, methodName);
+	ret->dllName = std::move(dllname);
 	return ret;
 }
 

+ 3 - 3
lib/CThreadHelper.cpp

@@ -1,9 +1,9 @@
 #include "StdInc.h"
 #include "CThreadHelper.h"
 
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 	#include <windows.h>
-#elif !defined(__APPLE__)
+#elif !defined(VCMI_APPLE)
 	#include <sys/prctl.h>
 #endif
 /*
@@ -49,7 +49,7 @@ void CThreadHelper::processTasks()
 // NOTE: on *nix string will be trimmed to 16 symbols
 void setThreadName(const std::string &name)
 {
-#ifdef _WIN32
+#ifdef VCMI_WINDOWS
 #ifndef __GNUC__
 	//follows http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
 	const DWORD MS_VC_EXCEPTION=0x406D1388;

+ 15 - 17
lib/CondSh.h

@@ -17,50 +17,48 @@ template <typename T> struct CondSh
 	boost::condition_variable cond;
 	boost::mutex mx;
 
-	CondSh()
-	{}
-
-	CondSh(T t)
-	{
-		data = t;
-	}
+	CondSh() {}
+	CondSh(T t) : data(t) {}
 
+	// set data
 	void set(T t)
 	{
 		boost::unique_lock<boost::mutex> lock(mx); 
-		data=t;
+		data = t;
 	} 
 
-	void setn(T t) //set data and notify
+	// set data and notify
+	void setn(T t)
 	{
-		{
-			boost::unique_lock<boost::mutex> lock(mx); 
-			data=t;
-		}
+		set(t);
 		cond.notify_all();
 	};
 
-	T get() //get stored value
+	// get stored value
+	T get()
 	{
 		boost::unique_lock<boost::mutex> lock(mx); 
 		return data;
 	}
 
-	void waitWhileTrue() //waits until data is set to false
+	// waits until data is set to false
+	void waitWhileTrue()
 	{
 		boost::unique_lock<boost::mutex> un(mx);
 		while(data)
 			cond.wait(un);
 	}
 
-	void waitWhile(const T &t) //waits while data is set to arg
+	// waits while data is set to arg
+	void waitWhile(const T & t)
 	{
 		boost::unique_lock<boost::mutex> un(mx);
 		while(data == t)
 			cond.wait(un);
 	}
 
-	void waitUntil(const T &t) //waits until data is set to arg
+	// waits until data is set to arg
+	void waitUntil(const T & t)
 	{
 		boost::unique_lock<boost::mutex> un(mx);
 		while(data != t)

+ 9 - 9
lib/Connection.cpp

@@ -347,7 +347,7 @@ void CSaveFile::putMagicBytes( const std::string &text )
 	write(text.c_str(), text.length());
 }
 
-CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
+CLoadFile::CLoadFile(const boost::filesystem::path & fname, int minimalVersion /*= version*/)
 {
 	registerTypes(*this);
 	openNextFile(fname, minimalVersion);
@@ -363,33 +363,33 @@ int CLoadFile::read(void * data, unsigned size)
 	return size;
 }
 
-void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
+void CLoadFile::openNextFile(const boost::filesystem::path & fname, int minimalVersion)
 {
 	assert(!reverseEndianess);
 	assert(minimalVersion <= version);
 
 	try
 	{
-		fName = fname;
-		sfile = make_unique<std::ifstream>(fname, std::ios::binary);
+		fName = fname.string();
+		sfile = make_unique<boost::filesystem::ifstream>(fname, std::ios::binary);
 		sfile->exceptions(std::ifstream::failbit | std::ifstream::badbit); //we throw a lot anyway
 
 		if(!(*sfile))
-			THROW_FORMAT("Error: cannot open to read %s!", fname);
+			THROW_FORMAT("Error: cannot open to read %s!", fName);
 
 		//we can read
 		char buffer[4];
 		sfile->read(buffer, 4);
 		if(std::memcmp(buffer,"VCMI",4))
-			THROW_FORMAT("Error: not a VCMI file(%s)!", fname);
+			THROW_FORMAT("Error: not a VCMI file(%s)!", fName);
 
 		*this >> fileVersion;	
 		if(fileVersion < minimalVersion)
-			THROW_FORMAT("Error: too old file format (%s)!", fname);
+			THROW_FORMAT("Error: too old file format (%s)!", fName);
 
 		if(fileVersion > version)
 		{
-			logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % fileVersion % version % fname;
+			logGlobal->warnStream() << boost::format("Warning format version mismatch: found %d when current is %d! (file %s)\n") % fileVersion % version % fName;
 
 			auto versionptr = (char*)&fileVersion;
 			std::reverse(versionptr, versionptr + 4);
@@ -401,7 +401,7 @@ void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
 				reverseEndianess = true;
 			}
 			else
-				THROW_FORMAT("Error: too new file format (%s)!", fname);
+				THROW_FORMAT("Error: too new file format (%s)!", fName);
 		}
 	}
 	catch(...)

+ 4 - 4
lib/Connection.h

@@ -1530,17 +1530,17 @@ class DLL_LINKAGE CLoadFile
 
 public:
 	std::string fName;
-	unique_ptr<std::ifstream> sfile;
+	unique_ptr<boost::filesystem::ifstream> sfile;
 
-	CLoadFile(const std::string &fname, int minimalVersion = version); //throws!
+	CLoadFile(const boost::filesystem::path & fname, int minimalVersion = version); //throws!
 	~CLoadFile();
 	int read(void * data, unsigned size); //throws!
 
-	void openNextFile(const std::string &fname, int minimalVersion); //throws!
+	void openNextFile(const boost::filesystem::path & fname, int minimalVersion); //throws!
 	void clear();
     void reportState(CLogger * out);
 
-	void checkMagicBytes(const std::string &text);
+	void checkMagicBytes(const std::string & text);
 };
 
 class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator>

+ 478 - 153
lib/VCMIDirs.cpp

@@ -1,6 +1,3 @@
-#include "StdInc.h"
-#include "VCMIDirs.h"
-
 /*
  * VCMIDirs.cpp, part of VCMI engine
  *
@@ -11,216 +8,504 @@
  *
  */
 
-static VCMIDirs VCMIDirsGlobal;
-
-VCMIDirs::VCMIDirs()
-{
-	// initialize local directory and create folders to which VCMI needs write access
-	boost::filesystem::create_directory(userDataPath());
-	boost::filesystem::create_directory(userCachePath());
-	boost::filesystem::create_directory(userConfigPath());
-	boost::filesystem::create_directory(userSavePath());
-}
+#include "StdInc.h"
+#include "VCMIDirs.h"
 
-VCMIDirs & VCMIDirs::get()
-{
-	return VCMIDirsGlobal;
-}
+namespace bfs = boost::filesystem;
 
-//FIXME: find way to at least decrease size of this ifdef (along with cleanup in CMake)
-#if defined(_WIN32)
+bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
 
-std::string VCMIDirs::userCachePath() const
+void IVCMIDirs::init()
 {
-	return userDataPath();
+	// TODO: Log errors
+	bfs::create_directory(userDataPath());
+	bfs::create_directory(userCachePath());
+	bfs::create_directory(userConfigPath());
+	bfs::create_directory(userSavePath());
 }
 
-std::string VCMIDirs::userConfigPath() const
+#ifdef VCMI_WINDOWS
+
+#include <Windows.h>
+#include <Shlobj.h>
+#include <Shellapi.h>
+
+// Generates script file named _temp.bat in 'to' directory and runs it
+// Script will:
+// - Wait util 'exeName' ends.
+// - Copy all files from 'from' to 'to'
+// - Ask user to replace files existed in 'to'.
+// - Run 'exeName'
+// - Delete itself.
+bool StartBatchCopyDataProgram(
+	const bfs::path& from, const bfs::path& to, const bfs::path& exeName,
+	const bfs::path& currentPath = bfs::current_path())
 {
-	return userDataPath() + "/config";
+	static const char base[] =
+		"@echo off"												"\n"
+		"echo Preparing to move VCMI data system."				"\n"
+
+		":CLIENT_RUNNING_LOOP"									"\n"
+		"TASKLIST | FIND /I %1% > nul"							"\n"
+		"IF ERRORLEVEL 1 ("										"\n"
+			"GOTO CLIENT_NOT_RUNNING"							"\n"
+		") ELSE ("												"\n"
+			"echo %1% is still running..."						"\n"
+			"echo Waiting until process ends..."				"\n"
+			"ping 1.1.1.1 -n 1 -w 3000 > nul"					"\n" // Sleep ~3 seconds. I love Windows :)
+			"goto :CLIENT_RUNNING_LOOP"							"\n"
+		")"														"\n"
+
+		":CLIENT_NOT_RUNNING"									"\n"
+		"echo %1% turned off..."								"\n"
+		"echo Attempt to move datas."							"\n"
+		"echo From: %2%"										"\n"
+		"echo To: %4%"											"\n"
+		"echo Please resolve any conflicts..."					"\n"
+		"move /-Y %3% %4%"										"\n" // Move all files from %3% to %4%.
+																	 // /-Y ask what to do when file exists in %4%
+		":REMOVE_OLD_DIR"										"\n"
+		"rd %2% || rem"											"\n" // Remove empty directory. Sets error flag if fail.
+		"IF ERRORLEVEL 145 ("									"\n" // Directory not empty
+			"echo Directory %2% is not empty."					"\n"
+			"echo Please move rest of files manually now."		"\n"
+			"pause"												"\n" // Press any key to continue...
+			"goto REMOVE_OLD_DIR"								"\n"
+		")"														"\n"
+		"echo Game data updated succefully."					"\n"
+		"echo Please update your shortcuts."					"\n"
+		"echo Press any key to start a game . . ."				"\n"
+		"pause > nul"											"\n"
+		"%5%"													"\n"
+		"del \"%%~f0\"&exit"									"\n" // Script deletes itself
+		;
+	
+	const auto startGameString =
+		bfs::equivalent(currentPath, from) ?
+		(boost::format("start \"\" %1%") % (to / exeName)) :						// Start game in new path.
+		(boost::format("start \"\" /D %1% %2%") % currentPath % (to / exeName));	// Start game in 'currentPath"
+
+	const bfs::path bathFilename = to / "_temp.bat";
+	bfs::ofstream bathFile(bathFilename, bfs::ofstream::trunc | bfs::ofstream::out);
+	if (!bathFile.is_open())
+		return false;
+	bathFile << (boost::format(base) % exeName % from % (from / "*.*") % to % startGameString.str()).str();
+	bathFile.close();
+
+	std::system(("start \"Updating VCMI datas\" /D \"" + to.string() + "\" \"" + bathFilename.string() + '\"').c_str());
+	// start won't block std::system
+	// /D start bat in other directory insteand of current directory.
+
+	return true;
 }
 
-std::string VCMIDirs::userSavePath() const
+class VCMIDirsWIN32 : public IVCMIDirs
 {
-	return userDataPath() + "/Games";
-}
+	public:
+		boost::filesystem::path userDataPath() const override;
+		boost::filesystem::path userCachePath() const override;
+		boost::filesystem::path userConfigPath() const override;
+
+		std::vector<boost::filesystem::path> dataPaths() const override;
+
+		boost::filesystem::path clientPath() const override;
+		boost::filesystem::path serverPath() const override;
 
-std::string VCMIDirs::userDataPath() const
+		boost::filesystem::path libraryPath() const override;
+		boost::filesystem::path binaryPath() const override;
+
+		std::string libraryName(const std::string& basename) const override;
+
+		std::string genHelpString() const override;
+
+		void init() override;
+	protected:
+		boost::filesystem::path oldUserDataPath() const;
+		boost::filesystem::path oldUserSavePath() const;
+};
+
+void VCMIDirsWIN32::init()
 {
-	const std::string homeDir = std::getenv("userprofile");
-	return homeDir + "\\vcmi";
-	//return dataPaths()[0];
+	// Call base (init dirs)
+	IVCMIDirs::init();
+
+	// Moves one directory (from) contents to another directory (to)
+	// Shows user the "moving file dialog" and ask to resolve conflits.
+	// If necessary updates current directory.
+	auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) -> bool
+	{
+		if (!bfs::is_directory(from))
+			return true; // Nothing to do here. Flies away.
+
+		if (bfs::is_empty(from))
+		{
+			if (bfs::current_path() == from)
+				bfs::current_path(to);
+
+			bfs::remove(from);
+			return true; // Nothing to do here. Flies away.
+		}
+
+		if (!bfs::is_directory(to))
+		{
+			// IVCMIDirs::init() should create all destination directories.
+			// TODO: Log fact, that we shouldn't be here.
+			bfs::create_directory(to);
+		}
+
+		// Why the hell path strings should be end with double null :/
+		auto makeDoubleNulled = [](const bfs::path& path) -> std::unique_ptr<wchar_t[]>
+		{
+			const std::wstring& pathStr = path.native();
+			std::unique_ptr<wchar_t[]> result(new wchar_t[pathStr.length() + 2]);
+			
+			size_t i = 0;
+			for (const wchar_t ch : pathStr)
+				result[i++] = ch;
+			result[i++] = L'\0';
+			result[i++] = L'\0';
+
+			return result;
+		};
+
+		auto fromDNulled = makeDoubleNulled(from / L"*.*");
+		auto toDNulled = makeDoubleNulled(to);
+
+		SHFILEOPSTRUCTW fileOp;
+		fileOp.hwnd = GetConsoleWindow();
+		fileOp.wFunc = FO_MOVE;
+		fileOp.pFrom = fromDNulled.get();
+		fileOp.pTo = toDNulled.get();
+		fileOp.fFlags = 0;
+		fileOp.hNameMappings = nullptr;
+		fileOp.lpszProgressTitle = nullptr;
+
+		const int errorCode = SHFileOperationW(&fileOp);
+		if (errorCode != 0) // TODO: Log error. User should try to move files.
+			return false;
+		else if (fileOp.fAnyOperationsAborted) // TODO: Log warn. User aborted operation. User should move files.
+			return false;
+		else if (!bfs::is_empty(from)) // TODO: Log warn. Some files not moved. User should try to move files.
+			return false;
+		
+		if (bfs::current_path() == from)
+			bfs::current_path(to);
+
+		// TODO: Log fact that we moved files succefully.
+		bfs::remove(from);
+		return true;
+	};
+	
+	// Retrieves the fully qualified path for the file that contains the specified module.
+	// The module must have been loaded by the current process.
+	// If this parameter is nullptr, retrieves the path of the executable file of the current process.
+	auto getModulePath = [](HMODULE hModule) -> bfs::path
+	{
+		wchar_t exePathW[MAX_PATH];
+		DWORD nSize = GetModuleFileNameW(hModule, exePathW, MAX_PATH);
+		DWORD error = GetLastError();
+		// WARN: Windows XP don't set ERROR_INSUFFICIENT_BUFFER error.
+		if (nSize != 0 && error != ERROR_INSUFFICIENT_BUFFER)
+			return bfs::path(std::wstring(exePathW, nSize));
+		// TODO: Error handling
+		return bfs::path();
+	};
+
+	// Moves one directory contents to another directory
+	// Shows user the "moving file dialog" and ask to resolve conflicts.
+	// It takes into account that 'from' path can contain current executable.
+	// If necessary closes program and starts update script.
+	auto advancedMoveDirIfExists = [getModulePath, moveDirIfExists](const bfs::path& from, const bfs::path& to) -> bool
+	{
+		const bfs::path executablePath = getModulePath(nullptr);
+
+		// VCMI cann't determine executable path.
+		// Use standard way to move directory and exit function.
+		if (executablePath.empty())
+			return moveDirIfExists(from, to);
+
+		const bfs::path executableName = executablePath.filename();
+
+		// Current executabl isn't in 'from' path.
+		// Use standard way to move directory and exit function.
+		if (!bfs::equivalent(executablePath, from / executableName))
+			return moveDirIfExists(from, to);
+
+		// Try standard way to move directory.
+		// I don't know how other systems, but Windows 8.1 allow to move running executable.
+		if (moveDirIfExists(from, to))
+			return true;
+
+		// Start copying script and exit program.
+		if (StartBatchCopyDataProgram(from, to, executableName))
+			exit(ERROR_SUCCESS);
+		
+		// Everything failed :C
+		return false;
+	};
+
+	moveDirIfExists(oldUserSavePath(), userSavePath());
+	advancedMoveDirIfExists(oldUserDataPath(), userDataPath());
 }
 
-std::string VCMIDirs::libraryPath() const
+bfs::path VCMIDirsWIN32::userDataPath() const
 {
+	wchar_t profileDir[MAX_PATH];
+
+	if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_MYDOCUMENTS, FALSE) != FALSE)
+		return bfs::path(profileDir) / "My Games\\vcmi";
+	
 	return ".";
 }
 
-std::string VCMIDirs::clientPath() const
+bfs::path VCMIDirsWIN32::oldUserDataPath() const
 {
-	return libraryPath() + "\\" + "VCMI_client.exe";
-}
+	wchar_t profileDir[MAX_PATH];
+	
+	if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed
+	{
+#if defined(_MSC_VER) && _MSC_VER >= 1700
+		wchar_t* buffer;
+		size_t bufferSize;
+		errno_t result = _wdupenv_s(&buffer, &bufferSize, L"userprofile");
+		if (result == 0)
+		{
+			bfs::path result(std::wstring(buffer, bufferSize));
+			free(buffer);
+			return result;
+		}
+#else
+		const char* profileDirA;
+		if (profileDirA = std::getenv("userprofile")) // STL way succeed
+			return bfs::path(profileDirA) / "vcmi";
+#endif
+		else
+			return "."; // Every thing failed, return current directory.
+	}
+	else
+		return bfs::path(profileDir) / "vcmi";
 
-std::string VCMIDirs::serverPath() const
-{
-	return libraryPath() + "\\" + "VCMI_server.exe";
+	//return dataPaths()[0] ???;
 }
+bfs::path VCMIDirsWIN32::oldUserSavePath() const { return userDataPath() / "Games"; }
 
-std::vector<std::string> VCMIDirs::dataPaths() const
-{
-	return std::vector<std::string>(1, ".");
-}
+bfs::path VCMIDirsWIN32::userCachePath() const { return userDataPath(); }
+bfs::path VCMIDirsWIN32::userConfigPath() const { return userDataPath() / "config"; }
 
-std::string VCMIDirs::libraryName(std::string basename) const
+std::vector<bfs::path> VCMIDirsWIN32::dataPaths() const
 {
-	return basename + ".dll";
+	return std::vector<bfs::path>(1, bfs::path("."));
 }
 
-#elif defined(__APPLE__)
+bfs::path VCMIDirsWIN32::clientPath() const { return binaryPath() / "VCMI_client.exe"; }
+bfs::path VCMIDirsWIN32::serverPath() const { return binaryPath() / "VCMI_server.exe"; }
 
-std::string VCMIDirs::userCachePath() const
-{
-	return userDataPath();
-}
+bfs::path VCMIDirsWIN32::libraryPath() const { return "."; }
+bfs::path VCMIDirsWIN32::binaryPath() const { return ".";  }
 
-std::string VCMIDirs::userConfigPath() const
+std::string VCMIDirsWIN32::genHelpString() const
 {
-	return userDataPath() + "/config";
-}
 
-std::string VCMIDirs::userSavePath() const
-{
-	return userDataPath() + "/Games";
+	std::vector<std::string> tempVec;
+	for (const bfs::path& path : dataPaths())
+		tempVec.push_back(path.string());
+	std::string gdStringA = boost::algorithm::join(tempVec, ";");
+
+
+	return
+		"  game data:   " + gdStringA + "\n"
+		"  libraries:   " + libraryPath().string() + "\n"
+		"  server:      " + serverPath().string() + "\n"
+		"\n"
+		"  user data:   " + userDataPath().string() + "\n"
+		"  user cache:  " + userCachePath().string() + "\n"
+		"  user config: " + userConfigPath().string() + "\n"
+		"  user saves:  " + userSavePath().string() + "\n"; // Should end without new-line?
 }
 
-std::string VCMIDirs::userDataPath() const
+std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { return basename + ".dll"; }
+#elif defined(VCMI_UNIX)
+class IVCMIDirsUNIX : public IVCMIDirs
 {
-	// 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];
-	// UserPath = path([urls[0] path] + "/vcmi").string();
+	public:
+		boost::filesystem::path clientPath() const override;
+		boost::filesystem::path serverPath() const override;
 
-	// ...so here goes a bit of hardcode instead
-	std::string home_dir = ".";
-	if (getenv("HOME") != nullptr )
-		home_dir = getenv("HOME");
+		std::string genHelpString() const override;
+};
 
-	return boost::filesystem::path(home_dir + "/Library/Application Support/vcmi").string();
-}
+bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }
+bfs::path IVCMIDirsUNIX::serverPath() const { return binaryPath() / "vcmiserver"; }
 
-std::string VCMIDirs::libraryPath() const
+std::string IVCMIDirsUNIX::genHelpString() const
 {
-	return ".";
-}
+	std::vector<std::string> tempVec;
+	for (const bfs::path& path : dataPaths())
+		tempVec.push_back(path.string());
+	std::string gdStringA = boost::algorithm::join(tempVec, ":");
 
-std::string VCMIDirs::clientPath() const
-{
-	return "./vcmiclient";
-}
 
-std::string VCMIDirs::serverPath() const
-{
-	return "./vcmiserver";
+	return
+		"  game data:   " + gdStringA + "\n"
+		"  libraries:   " + libraryPath().string() + "\n"
+		"  server:      " + serverPath().string() + "\n"
+		"\n"
+		"  user data:   " + userDataPath().string() + "\n"
+		"  user cache:  " + userCachePath().string() + "\n"
+		"  user config: " + userConfigPath().string() + "\n"
+		"  user saves:  " + userSavePath().string() + "\n"; // Should end without new-line?
 }
 
-std::vector<std::string> VCMIDirs::dataPaths() const
+#ifdef VCMI_APPLE
+class VCMIDirsOSX : public IVCMIDirsUNIX
 {
-	return std::vector<std::string>(1, "../Data");
-}
+	public:
+		boost::filesystem::path userDataPath() const override;
+		boost::filesystem::path userCachePath() const override;
+		boost::filesystem::path userConfigPath() const override;
 
-std::string VCMIDirs::libraryName(std::string basename) const
-{
-	return "lib" + basename + ".dylib";
-}
+		std::vector<boost::filesystem::path> dataPaths() const override;
 
-#else
+		boost::filesystem::path libraryPath() const override;
+		boost::filesystem::path binaryPath() const override;
 
-std::string VCMIDirs::libraryName(std::string basename) const
-{
-	return "lib" + basename + ".so";
-}
+		std::string libraryName(const std::string& basename) const override;
 
-std::string VCMIDirs::libraryPath() const
+		void init() override;
+};
+
+void VCMIDirsOSX::init()
 {
-	return M_LIB_DIR;
+	// Call base (init dirs)
+	IVCMIDirsUNIX::init();
+
+	auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to)
+	{
+		if (!bfs::is_directory(from))
+			return; // Nothing to do here. Flies away.
+
+		if (bfs::is_empty(from))
+		{
+			bfs::remove(from);
+			return; // Nothing to do here. Flies away.
+		}
+
+		if (!bfs::is_directory(to))
+		{
+			// IVCMIDirs::init() should create all destination directories.
+			// TODO: Log fact, that we shouldn't be here.
+			bfs::create_directory(to);
+		}
+
+		for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file)
+		{
+			const boost::filesystem::path& srcFilePath = file->path();
+			const boost::filesystem::path  dstFilePath = to / srcFilePath.filename();
+
+			// TODO: Aplication should ask user what to do when file exists:
+			// replace/ignore/stop process/replace all/ignore all
+			if (!boost::filesystem::exists(dstFilePath))
+				bfs::rename(srcFilePath, dstFilePath);
+		}
+
+		if (!bfs::is_empty(from)); // TODO: Log warn. Some files not moved. User should try to move files.
+		else
+			bfs::remove(from);
+	};
+
+	moveDirIfExists(userDataPath() / "Games", userSavePath());
 }
 
-std::string VCMIDirs::clientPath() const
+bfs::path VCMIDirsOSX::userDataPath() const
 {
-	return std::string(M_BIN_DIR) + "/" + "vcmiclient";
+	// 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];
+	// UserPath = path([urls[0] path] + "/vcmi").string();
+
+	// ...so here goes a bit of hardcode instead
+
+	const char* homeDir = getenv("HOME"); // Should be std::getenv?
+	if (homeDir == nullptr)
+		homeDir = ".";
+	return bfs::path(homeDir) / "Library" / "Application Support" / "vcmi";
 }
+bfs::path VCMIDirsOSX::userCachePath() const { return userDataPath(); }
+bfs::path VCMIDirsOSX::userConfigPath() const { return userDataPath() / "config"; }
 
-std::string VCMIDirs::serverPath() const
+std::vector<bfs::path> VCMIDirsOSX::dataPaths() const
 {
-	return std::string(M_BIN_DIR) + "/" + "vcmiserver";
+	return std::vector<bfs::path>(1, "../Data");
 }
 
-// $XDG_DATA_HOME, default: $HOME/.local/share
-std::string VCMIDirs::userDataPath() const
+bfs::path VCMIDirsOSX::libraryPath() const { return "."; }
+bfs::path VCMIDirsOSX::binaryPath() const { return "."; }
+
+std::string libraryName(const std::string& basename) { return "lib" + basename + ".dylib"; }
+#elif defined(VCMI_LINUX)
+class VCMIDirsLinux : public IVCMIDirsUNIX
 {
-#ifdef __ANDROID__
-	// on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/
-	return std::string(getenv("HOME"));
-#else
-	if (getenv("XDG_DATA_HOME") != nullptr )
-		return std::string(getenv("XDG_DATA_HOME")) + "/vcmi";
-	if (getenv("HOME") != nullptr )
-		return std::string(getenv("HOME")) + "/.local/share" + "/vcmi";
-	return ".";
-#endif
-}
+public:
+	boost::filesystem::path userDataPath() const override;
+	boost::filesystem::path userCachePath() const override;
+	boost::filesystem::path userConfigPath() const override;
+
+	std::vector<boost::filesystem::path> dataPaths() const override;
 
-std::string VCMIDirs::userSavePath() const
+	boost::filesystem::path libraryPath() const override;
+	boost::filesystem::path binaryPath() const override;
+
+	std::string libraryName(const std::string& basename) const override;
+};
+
+bfs::path VCMIDirsLinux::userDataPath() const
 {
-	return userDataPath() + "/Saves";
+	// $XDG_DATA_HOME, default: $HOME/.local/share
+	const char* homeDir;
+	if ((homeDir = getenv("XDG_DATA_HOME")))
+		return homeDir;
+	else if ((homeDir = getenv("HOME")))
+		return bfs::path(homeDir) / ".local" / "share" / "vcmi";
+	else
+		return ".";
 }
-
-// $XDG_CACHE_HOME, default: $HOME/.cache
-std::string VCMIDirs::userCachePath() const
+bfs::path VCMIDirsLinux::userCachePath() const
 {
-#ifdef __ANDROID__
-	return userDataPath() + "/cache";
-#else
-	if (getenv("XDG_CACHE_HOME") != nullptr )
-		return std::string(getenv("XDG_CACHE_HOME")) + "/vcmi";
-	if (getenv("HOME") != nullptr )
-		return std::string(getenv("HOME")) + "/.cache" + "/vcmi";
-	return ".";
-#endif
+	// $XDG_CACHE_HOME, default: $HOME/.cache
+	const char* tempResult;
+	if ((tempResult = getenv("XDG_CACHE_HOME")))
+		return bfs::path(tempResult) / "vcmi";
+	else if ((tempResult = getenv("HOME")))
+		return bfs::path(tempResult) / ".cache" / "vcmi";
+	else
+		return ".";
 }
-
-// $XDG_CONFIG_HOME, default: $HOME/.config
-std::string VCMIDirs::userConfigPath() const
+bfs::path VCMIDirsLinux::userConfigPath() const
 {
-#ifdef __ANDROID__
-	return userDataPath() + "/config";
-#else
-	if (getenv("XDG_CONFIG_HOME") != nullptr )
-		return std::string(getenv("XDG_CONFIG_HOME")) + "/vcmi";
-	if (getenv("HOME") != nullptr )
-		return std::string(getenv("HOME")) + "/.config" + "/vcmi";
-	return ".";
-#endif
+	// $XDG_CONFIG_HOME, default: $HOME/.config
+	const char* tempResult;
+	if ((tempResult = getenv("XDG_CONFIG_HOME")))
+		return bfs::path(tempResult) / "vcmi";
+	else if ((tempResult = getenv("HOME")))
+		return bfs::path(tempResult) / ".config" / "vcmi";
+	else
+		return ".";
 }
 
-// $XDG_DATA_DIRS, default: /usr/local/share/:/usr/share/
-std::vector<std::string> VCMIDirs::dataPaths() const
+std::vector<bfs::path> VCMIDirsLinux::dataPaths() const
 {
+	// $XDG_DATA_DIRS, default: /usr/local/share/:/usr/share/
+
 	// construct list in reverse.
 	// in specification first directory has highest priority
 	// in vcmi fs last directory has highest priority
+	std::vector<bfs::path> ret;
 
-	std::vector<std::string> ret;
-#ifdef __ANDROID__
-	ret.push_back(userDataPath());
-#else
-	if (getenv("HOME") != nullptr ) // compatibility, should be removed after 0.96
-		ret.push_back(std::string(getenv("HOME")) + "/.vcmi");
+	const char* tempResult;
 	ret.push_back(M_DATA_DIR);
 
-	if (getenv("XDG_DATA_DIRS") != nullptr)
+	if ((tempResult = getenv("XDG_DATA_DIRS")) != nullptr)
 	{
-		std::string dataDirsEnv = getenv("XDG_DATA_DIRS");
+		std::string dataDirsEnv = tempResult;
 		std::vector<std::string> dataDirs;
 		boost::split(dataDirs, dataDirsEnv, boost::is_any_of(":"));
 		for (auto & entry : boost::adaptors::reverse(dataDirs))
@@ -231,21 +516,61 @@ std::vector<std::string> VCMIDirs::dataPaths() const
 		ret.push_back("/usr/share/");
 		ret.push_back("/usr/local/share/");
 	}
-#endif
+
 	return ret;
 }
 
-#endif
+bfs::path VCMIDirsLinux::libraryPath() const { return M_LIB_DIR; }
+bfs::path VCMIDirsLinux::binaryPath() const { return M_BIN_DIR; }
 
-std::string VCMIDirs::genHelpString() const
+std::string VCMIDirsLinux::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
+#ifdef VCMI_ANDROID
+class VCMIDirsAndroid : public VCMIDirsLinux
 {
-	return
-	"  game data:   " + boost::algorithm::join(dataPaths(), ":") + "\n" +
-	"  libraries:   " + libraryPath() + "\n" +
-	"  server:      " + serverPath() + "\n" +
-	"\n" +
-	"  user data:   " + userDataPath() + "\n" +
-	"  user cache:  " + userCachePath() + "\n" +
-	"  user config: " + userConfigPath() + "\n" +
-	"  user saves:  " + userSavePath() + "\n";
+public:
+	boost::filesystem::path userDataPath() const override;
+	boost::filesystem::path userCachePath() const override;
+	boost::filesystem::path userConfigPath() const override;
+
+	std::vector<boost::filesystem::path> dataPaths() const override;
+};
+
+// on Android HOME will be set to something like /sdcard/data/Android/is.xyz.vcmi/files/
+bfs::path VCMIDirsAndroid::userDataPath() const { return getenv("HOME"); }
+bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; }
+bfs::path VCMIDirsAndroid::userConfigPath() const { return userDataPath() / "config"; }
+
+std::vector<bfs::path> VCMIDirsAndroid::dataPaths() const
+{
+	return std::vector<bfs::path>(1, userDataPath());
 }
+#endif // VCMI_ANDROID
+#endif // VCMI_APPLE, VCMI_LINUX
+#endif // VCMI_WINDOWS, VCMI_UNIX
+
+// Getters for interfaces are separated for clarity.
+namespace VCMIDirs
+{
+	const IVCMIDirs& get()
+	{
+		#ifdef VCMI_WINDOWS
+			static VCMIDirsWIN32 singleton;
+		#elif defined(VCMI_ANDROID)
+			static VCMIDirsAndroid singleton;
+		#elif defined(VCMI_LINUX)
+			static VCMIDirsLinux singleton;
+		#elif defined(VCMI_APPLE)
+			static VCMIDirsOSX singleton;
+		#endif
+		static bool initialized = false;
+		if (!initialized)
+		{
+			std::locale::global(boost::locale::generator().generate("en_US.UTF-8"));
+			boost::filesystem::path::imbue(std::locale());
+
+			singleton.init();
+			initialized = true;
+		}
+		return singleton;
+	}
+}

+ 44 - 38
lib/VCMIDirs.h

@@ -1,52 +1,58 @@
+/*
+* VCMIDirs.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
 #pragma once
 
-#include "GameConstants.h"
-
-/*
- * VCMIDirs.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-/// Where to find the various VCMI files. This is mostly useful for linux. 
-class DLL_LINKAGE VCMIDirs
+class DLL_LINKAGE IVCMIDirs
 {
-public:
-	VCMIDirs();
+	public:
+		// Path to user-specific data directory
+		virtual boost::filesystem::path userDataPath() const = 0;
 
-	/// get singleton instance
-	static VCMIDirs & get();
+		// Path to "cache" directory, can be used for any non-essential files
+		virtual boost::filesystem::path userCachePath() const = 0;
 
-	/// Path to user-specific data directory
-	std::string userDataPath() const;
+		// Path to writeable directory with user configs
+		virtual boost::filesystem::path userConfigPath() const = 0;
 
-	/// Path to "cache" directory, can be used for any non-essential files
-	std::string userCachePath() const;
+		// Path to saved games
+		virtual boost::filesystem::path userSavePath() const;
 
-	/// Path to writeable directory with user configs
-	std::string userConfigPath() const;
+		// Paths to global system-wide data directories. First items have higher priority
+		virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
 
-	/// Path to saved games
-	std::string userSavePath() const;
+		// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
+		virtual boost::filesystem::path clientPath() const = 0;
 
-	/// Paths to global system-wide data directories. First items have higher priority
-	std::vector<std::string> dataPaths() const;
+		// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
+		virtual boost::filesystem::path serverPath() const = 0;
 
-	/// Full path to client executable, including server name (e.g. /usr/bin/vcmiclient)
-	std::string clientPath() const;
+		// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
+		virtual boost::filesystem::path libraryPath() const = 0;
 
-	/// Full path to server executable, including server name (e.g. /usr/bin/vcmiserver)
-	std::string serverPath() const;
+		// Path where vcmi binaries can be found
+		virtual boost::filesystem::path binaryPath() const = 0;
 
-	/// Path where vcmi libraries can be found (in AI and Scripting subdirectories)
-	std::string libraryPath() const;
+		// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll")
+		virtual std::string libraryName(const std::string& basename) const = 0;
+		// virtual std::string libraryName(const char* basename) const = 0; ?
+		// virtual std::string libraryName(std::string&& basename) const = 0;?
 
-	/// Returns system-specific name for dynamic libraries ( StupidAI => "libStupidAI.so" or "StupidAI.dll")
-	std::string libraryName(std::string basename) const;
-
-	std::string genHelpString() const;
+		virtual std::string genHelpString() const = 0;
+		
+		// Creates not existed, but required directories.
+		// Updates directories what change name/path between versions.
+		// Function called automatically.
+		virtual void init();
 };
+
+namespace VCMIDirs
+{
+	extern DLL_LINKAGE const IVCMIDirs& get();
+}

+ 10 - 19
lib/filesystem/CArchiveLoader.cpp

@@ -13,9 +13,9 @@ ArchiveEntry::ArchiveEntry()
 
 }
 
-CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string & archive):
-    archive(archive),
-    mountPoint(mountPoint)
+CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) :
+    archive(std::move(_archive)),
+    mountPoint(std::move(_mountPoint))
 {
 	// Open archive file(.snd, .vid, .lod)
 	CFileInputStream fileStream(archive);
@@ -25,28 +25,19 @@ CArchiveLoader::CArchiveLoader(const std::string &mountPoint, const std::string
 		return;
 
 	// Retrieve file extension of archive in uppercase
-	CFileInfo fileInfo(archive);
-	std::string ext = fileInfo.getExtension();
-	boost::to_upper(ext);
+	const std::string ext = boost::to_upper_copy(archive.extension().string());
 
 	// Init the specific lod container format
 	if(ext == ".LOD" || ext == ".PAC")
-	{
 		initLODArchive(mountPoint, fileStream);
-	}
 	else if(ext == ".VID")
-	{
 		initVIDArchive(mountPoint, fileStream);
-	}
 	else if(ext == ".SND")
-	{
 		initSNDArchive(mountPoint, fileStream);
-	}
 	else
-	{
-		throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive);
-	}
-	logGlobal->traceStream() << ext << "Archive loaded, " << entries.size() << " files found";
+		throw std::runtime_error("LOD archive format unknown. Cannot deal with " + archive.string());
+
+	logGlobal->traceStream() << ext << "Archive \""<<archive.filename()<<"\" loaded (" << entries.size() << " files found).";
 }
 
 void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStream & fileStream)
@@ -61,7 +52,7 @@ void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStr
 	fileStream.seek(0x5c);
 
 	// Insert entries to list
-	for(ui32 i = 0; i < totalFiles; i++)
+	for(ui32 i = 0; i < totalFiles; ++i)
 	{
 		char filename[16];
 		reader.read(reinterpret_cast<ui8*>(filename), 16);
@@ -90,7 +81,7 @@ void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStr
 	std::set<int> offsets;
 
 	// Insert entries to list
-	for(ui32 i = 0; i < totalFiles; i++)
+	for(ui32 i = 0; i < totalFiles; ++i)
 	{
 		char filename[40];
 		reader.read(reinterpret_cast<ui8*>(filename), 40);
@@ -122,7 +113,7 @@ void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStr
 	ui32 totalFiles = reader.readUInt32();
 
 	// Insert entries to list
-	for(ui32 i = 0; i < totalFiles; i++)
+	for(ui32 i = 0; i < totalFiles; ++i)
 	{
 		char filename[40];
 		reader.read(reinterpret_cast<ui8*>(filename), 40);

+ 2 - 2
lib/filesystem/CArchiveLoader.h

@@ -55,7 +55,7 @@ public:
 	 *
 	 * @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
 	 */
-	explicit CArchiveLoader(const std::string & mountPoint, const std::string & archive);
+	CArchiveLoader(std::string mountPoint, boost::filesystem::path archive);
 
 	/// Interface implementation
 	/// @see ISimpleResourceLoader
@@ -87,7 +87,7 @@ private:
 	void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
 
 	/** The file path to the archive which is scanned and indexed. */
-	std::string archive;
+	boost::filesystem::path archive;
 
 	std::string mountPoint;
 

+ 15 - 14
lib/filesystem/CFileInfo.cpp

@@ -41,17 +41,17 @@ std::string CFileInfo::getPath() const
 std::string CFileInfo::getExtension() const
 {
 	// Get position of file extension dot
-	size_t dotPos = name.find_last_of("/.");
+	size_t dotPos = name.find_last_of('.');
 
-	if(dotPos != std::string::npos && name[dotPos] == '.')
+	if(dotPos != std::string::npos)
 		return name.substr(dotPos);
-	else
-		return "";
+
+	return "";
 }
 
 std::string CFileInfo::getFilename() const
 {
-	size_t found = name.find_last_of("/\\");
+	const size_t found = name.find_last_of("/\\");
 	return name.substr(found + 1);
 }
 
@@ -60,9 +60,9 @@ std::string CFileInfo::getStem() const
 	std::string rslt = name;
 
 	// Remove file extension
-	size_t dotPos = name.find_last_of("/.");
+	const size_t dotPos = name.find_last_of('.');
 
-	if(dotPos != std::string::npos && name[dotPos] == '.')
+	if(dotPos != std::string::npos)
 		rslt.erase(dotPos);
 
 	return rslt;
@@ -70,18 +70,19 @@ std::string CFileInfo::getStem() const
 
 std::string CFileInfo::getBaseName() const
 {
-	size_t begin = name.find_last_of("/");
-	size_t end = name.find_last_of("/.");
-
-	if(end != std::string::npos && name[end] == '/')
-		end = std::string::npos;
+	size_t begin = name.find_last_of("/\\");
+	size_t end = name.find_last_of(".");
 
 	if(begin == std::string::npos)
 		begin = 0;
 	else
-		begin++;
+		++begin;
+	
+	if (end < begin)
+		end = std::string::npos;
 
-	return name.substr(begin, end - begin);
+	size_t len = (end == std::string::npos ? std::string::npos : end - begin);
+	return name.substr(begin, len);
 }
 
 EResType::Type CFileInfo::getType() const

+ 4 - 6
lib/filesystem/CFileInputStream.cpp

@@ -3,7 +3,7 @@
 
 #include "CFileInfo.h"
 
-CFileInputStream::CFileInputStream(const std::string & file, si64 start, si64 size)
+CFileInputStream::CFileInputStream(const boost::filesystem::path & file, si64 start, si64 size)
 {
 	open(file, start, size);
 }
@@ -18,14 +18,12 @@ CFileInputStream::~CFileInputStream()
 	fileStream.close();
 }
 
-void CFileInputStream::open(const std::string & file, si64 start, si64 size)
+void CFileInputStream::open(const boost::filesystem::path & file, si64 start, si64 size)
 {
-	fileStream.open(file.c_str(), std::ios::in | std::ios::binary);
+	fileStream.open(file, std::ios::in | std::ios::binary);
 
 	if (fileStream.fail())
-	{
-		throw std::runtime_error("File " + file + " isn't available.");
-	}
+		throw std::runtime_error("File " + file.string() + " isn't available.");
 
 	dataStart = start;
 	dataSize = size;

+ 3 - 3
lib/filesystem/CFileInputStream.h

@@ -25,7 +25,7 @@ public:
 	 *
 	 * @see CFileInputStream::open
 	 */
-	CFileInputStream(const std::string & file, si64 start=0, si64 size=0);
+	CFileInputStream(const boost::filesystem::path & file, si64 start = 0, si64 size = 0);
 
 	/**
 	 * C-tor. Opens the specified file.
@@ -88,11 +88,11 @@ private:
 	 *
 	 * @throws std::runtime_error if file wasn't found
 	 */
-	void open(const std::string & file, si64 start, si64 size);
+	void open(const boost::filesystem::path & file, si64 start, si64 size);
 
 	si64 dataStart;
 	si64 dataSize;
 
 	/** Native c++ input file stream object. */
-	std::ifstream fileStream;
+	boost::filesystem::ifstream fileStream;
 };

+ 56 - 30
lib/filesystem/CFilesystemLoader.cpp

@@ -4,9 +4,11 @@
 #include "CFileInfo.h"
 #include "CFileInputStream.h"
 
-CFilesystemLoader::CFilesystemLoader(const std::string &mountPoint, const std::string & baseDirectory, size_t depth, bool initial):
-    baseDirectory(baseDirectory),
-    mountPoint(mountPoint),
+namespace bfs = boost::filesystem;
+
+CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDirectory, size_t depth, bool initial):
+    baseDirectory(std::move(baseDirectory)),
+	mountPoint(std::move(_mountPoint)),
     fileList(listFiles(mountPoint, depth, initial))
 {
 	logGlobal->traceStream() << "Filesystem loaded, " << fileList.size() << " files found";
@@ -16,7 +18,7 @@ std::unique_ptr<CInputStream> CFilesystemLoader::load(const ResourceID & resourc
 {
 	assert(fileList.count(resourceName));
 
-	std::unique_ptr<CInputStream> stream(new CFileInputStream(baseDirectory + '/' + fileList.at(resourceName)));
+	std::unique_ptr<CInputStream> stream(new CFileInputStream(baseDirectory / fileList.at(resourceName)));
 	return stream;
 }
 
@@ -34,7 +36,7 @@ boost::optional<std::string> CFilesystemLoader::getResourceName(const ResourceID
 {
 	assert(existsResource(resourceName));
 
-	return baseDirectory + '/' + fileList.at(resourceName);
+	return (baseDirectory / fileList.at(resourceName)).string();
 }
 
 std::unordered_set<ResourceID> CFilesystemLoader::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
@@ -45,7 +47,8 @@ std::unordered_set<ResourceID> CFilesystemLoader::getFilteredFiles(std::function
 	{
 		if (filter(file.first))
 			foundID.insert(file.first);
-	}	return foundID;
+	}
+	return foundID;
 }
 
 bool CFilesystemLoader::createResource(std::string filename, bool update)
@@ -65,7 +68,7 @@ bool CFilesystemLoader::createResource(std::string filename, bool update)
 
 	if (!update)
 	{
-		std::ofstream newfile (baseDirectory + "/" + filename);
+		bfs::ofstream newfile(baseDirectory / filename);
 		if (!newfile.good())
 			return false;
 	}
@@ -73,49 +76,72 @@ bool CFilesystemLoader::createResource(std::string filename, bool update)
 	return true;
 }
 
-std::unordered_map<ResourceID, std::string> CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const
+std::unordered_map<ResourceID, bfs::path> CFilesystemLoader::listFiles(const std::string &mountPoint, size_t depth, bool initial) const
 {
-	std::set<EResType::Type> initialTypes;
-	initialTypes.insert(EResType::DIRECTORY);
-	initialTypes.insert(EResType::TEXT);
-	initialTypes.insert(EResType::ARCHIVE_LOD);
-	initialTypes.insert(EResType::ARCHIVE_VID);
-	initialTypes.insert(EResType::ARCHIVE_SND);
-	initialTypes.insert(EResType::ARCHIVE_ZIP);
+	static const EResType::Type initArray[] = {
+		EResType::DIRECTORY,
+		EResType::TEXT,
+		EResType::ARCHIVE_LOD,
+		EResType::ARCHIVE_VID,
+		EResType::ARCHIVE_SND,
+		EResType::ARCHIVE_ZIP };
+	static const std::set<EResType::Type> initialTypes(initArray, initArray + ARRAY_COUNT(initArray));
 
-	assert(boost::filesystem::is_directory(baseDirectory));
-	std::unordered_map<ResourceID, std::string> fileList;
+	assert(bfs::is_directory(baseDirectory));
+	std::unordered_map<ResourceID, bfs::path> fileList;
 
-	std::vector<std::string> path;//vector holding relative path to our file
+	std::vector<bfs::path> path; //vector holding relative path to our file
 
-	boost::filesystem::recursive_directory_iterator enddir;
-	boost::filesystem::recursive_directory_iterator it(baseDirectory, boost::filesystem::symlink_option::recurse);
+	bfs::recursive_directory_iterator enddir;
+	bfs::recursive_directory_iterator it(baseDirectory, bfs::symlink_option::recurse);
 
 	for(; it != enddir; ++it)
 	{
 		EResType::Type type;
 
-		if (boost::filesystem::is_directory(it->status()))
+		if (bfs::is_directory(it->status()))
 		{
-			path.resize(it.level()+1);
-			path.back() = it->path().leaf().string();
+			path.resize(it.level() + 1);
+			path.back() = it->path().filename();
 			// don't iterate into directory if depth limit reached
 			it.no_push(depth <= it.level());
 
 			type = EResType::DIRECTORY;
 		}
 		else
-			type = EResTypeHelper::getTypeFromExtension(boost::filesystem::extension(*it));
+			type = EResTypeHelper::getTypeFromExtension(it->path().extension().string());
 
 		if (!initial || vstd::contains(initialTypes, type))
 		{
 			//reconstruct relative filename (not possible via boost AFAIK)
-			std::string filename;
-			for (size_t i=0; i<it.level() && i<path.size(); i++)
-				filename += path[i] + '/';
-			filename += it->path().leaf().string();
-
-			fileList[ResourceID(mountPoint + filename, type)] = filename;
+			bfs::path filename;
+			const size_t iterations = std::min((size_t)it.level(), path.size());
+			if (iterations)
+			{
+				filename = path.front();
+				for (size_t i = 1; i < iterations; ++i)
+					filename /= path[i];
+				filename /= it->path().filename();
+			}
+			else
+				filename = it->path().filename();
+
+			std::string resName;
+			if (bfs::path::preferred_separator != '/')
+			{
+				// resource names are using UNIX slashes (/)
+				resName.reserve(resName.size() + filename.native().size());
+				resName = mountPoint;
+				for (const char c : filename.string())
+					if (c != bfs::path::preferred_separator)
+						resName.push_back(c);
+					else
+						resName.push_back('/');
+			}
+			else
+				resName = mountPoint + filename.string();
+
+			fileList[ResourceID(resName, type)] = std::move(filename);
 		}
 	}
 

+ 5 - 5
lib/filesystem/CFilesystemLoader.h

@@ -30,7 +30,7 @@ public:
 	 *
 	 * @throws std::runtime_error if the base directory is not a directory or if it is not available
 	 */
-	explicit CFilesystemLoader(const std::string & mountPoint, const std::string & baseDirectory, size_t depth = 16, bool initial = false);
+	explicit CFilesystemLoader(std::string mountPoint, boost::filesystem::path baseDirectory, size_t depth = 16, bool initial = false);
 
 	/// Interface implementation
 	/// @see ISimpleResourceLoader
@@ -39,11 +39,11 @@ public:
 	std::string getMountPoint() const override;
 	bool createResource(std::string filename, bool update = false) override;
 	boost::optional<std::string> getResourceName(const ResourceID & resourceName) const override;
-	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const;
+	std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
 
 private:
 	/** The base directory which is scanned and indexed. */
-	std::string baseDirectory;
+	boost::filesystem::path baseDirectory;
 
 	std::string mountPoint;
 
@@ -51,7 +51,7 @@ private:
 	 * key = ResourceID for resource loader
 	 * value = name that can be used to access file
 	*/
-	std::unordered_map<ResourceID, std::string> fileList;
+	std::unordered_map<ResourceID, boost::filesystem::path> fileList;
 
 	/**
 	 * Returns a list of pathnames denoting the files in the directory denoted by this pathname.
@@ -62,5 +62,5 @@ private:
 	 * @return a list of pathnames denoting the files and directories in the directory denoted by this pathname
 	 * The array will be empty if the directory is empty. Ptr is null if the directory doesn't exist or if it isn't a directory.
 	 */
-	std::unordered_map<ResourceID, std::string> listFiles(const std::string &mountPoint, size_t depth, bool initial) const;
+	std::unordered_map<ResourceID, boost::filesystem::path> listFiles(const std::string &mountPoint, size_t depth, bool initial) const;
 };

+ 2 - 2
lib/filesystem/Filesystem.cpp

@@ -69,7 +69,7 @@ void CFilesystemGenerator::loadDirectory(const std::string &mountPoint, const Js
 	std::string URI = prefix + config["path"].String();
 	int depth = 16;
 	if (!config["depth"].isNull())
-		depth = config["depth"].Float();
+		depth = (int)config["depth"].Float();
 
 	ResourceID resID(URI, EResType::DIRECTORY);
 
@@ -130,7 +130,7 @@ ISimpleResourceLoader * CResourceHandler::createInitial()
 			auto filename = loader->getResourceName(ID);
 			if (filename)
 			{
-				auto dir = new CFilesystemLoader(URI + "/", *filename, depth, true);
+				auto dir = new CFilesystemLoader(URI + '/', *filename, depth, true);
 				initialLoader->addLoader(dir, false);
 			}
 		}

+ 8 - 6
lib/int3.h

@@ -94,11 +94,11 @@ public:
 	}
 
 	//returns squared distance on Oxy plane (z coord is not used)
-	si32 dist2dSQ(const int3 & o) const
+	ui32 dist2dSQ(const int3 & o) const
 	{
 		const si32 dx = (x - o.x);
 		const si32 dy = (y - o.y);
-		return dx*dx + dy*dy;
+		return (ui32)(dx*dx) + (ui32)(dy*dy);
 	}
 	//returns distance on Oxy plane (z coord is not used)
 	double dist2d(const int3 & o) const
@@ -157,15 +157,17 @@ struct ShashInt3
 static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
 	int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
 
-//FIXME: make sure it's <int3> container and not just any
 template<typename Container>
 int3 findClosestTile (Container & container, int3 dest)
 {
-	int3 result(-1,-1,-1);
+	static_assert(std::is_same<typename Container::value_type, int3>::value,
+		"findClosestTile requires <int3> container.");
+
+	int3 result(-1, -1, -1);
 	ui32 distance = std::numeric_limits<ui32>::max();
-	for (int3 tile : container)
+	for (const int3& tile : container)
 	{
-		ui32 currentDistance = dest.dist2dSQ(tile);
+		const ui32 currentDistance = dest.dist2dSQ(tile);
 		if (currentDistance < distance)
 		{
 			result = tile;

+ 8 - 16
lib/logging/CBasicLogConfigurator.cpp

@@ -3,11 +3,8 @@
 
 #include "../CConfigHandler.h"
 
-CBasicLogConfigurator::CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console) : filePath(filePath),
-	console(console), appendToLogFile(false)
-{
-
-}
+CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path filePath, CConsoleHandler * const console) :
+	filePath(std::move(filePath)), console(console), appendToLogFile(false) {}
 
 void CBasicLogConfigurator::configureDefault()
 {
@@ -21,7 +18,8 @@ void CBasicLogConfigurator::configure()
 	try
 	{
 		const JsonNode & loggingNode = settings["logging"];
-		if(loggingNode.isNull()) throw std::runtime_error("Settings haven't been loaded.");
+		if(loggingNode.isNull())
+			throw std::runtime_error("Settings haven't been loaded.");
 
 		// Configure loggers
 		const JsonNode & loggers = loggingNode["loggers"];
@@ -87,7 +85,7 @@ void CBasicLogConfigurator::configure()
 	logGlobal->infoStream() << "Initialized logging system based on settings successfully.";
 }
 
-ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & level) const
+ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & level)
 {
 	static const std::map<std::string, ELogLevel::ELogLevel> levelMap = boost::assign::map_list_of
 			("trace", ELogLevel::TRACE)
@@ -95,18 +93,15 @@ ELogLevel::ELogLevel CBasicLogConfigurator::getLogLevel(const std::string & leve
 			("info", ELogLevel::INFO)
 			("warn", ELogLevel::WARN)
 			("error", ELogLevel::ERROR);
+	
 	const auto & levelPair = levelMap.find(level);
 	if(levelPair != levelMap.end())
-	{
 		return levelPair->second;
-	}
 	else
-	{
 		throw std::runtime_error("Log level " + level + " unknown.");
-	}
 }
 
-EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName) const
+EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(const std::string & colorName)
 {
 	static const std::map<std::string, EConsoleTextColor::EConsoleTextColor> colorMap = boost::assign::map_list_of
 			("default", EConsoleTextColor::DEFAULT)
@@ -117,13 +112,10 @@ EConsoleTextColor::EConsoleTextColor CBasicLogConfigurator::getConsoleColor(cons
 			("white", EConsoleTextColor::WHITE)
 			("gray", EConsoleTextColor::GRAY)
 			("teal", EConsoleTextColor::TEAL);
+
 	const auto & colorPair = colorMap.find(colorName);
 	if(colorPair != colorMap.end())
-	{
 		return colorPair->second;
-	}
 	else
-	{
 		throw std::runtime_error("Color " + colorName + " unknown.");
-	}
 }

+ 9 - 6
lib/logging/CBasicLogConfigurator.h

@@ -22,7 +22,7 @@ class JsonNode;
 class DLL_LINKAGE CBasicLogConfigurator
 {
 public:
-	CBasicLogConfigurator(const std::string & filePath, CConsoleHandler * console);
+	CBasicLogConfigurator(boost::filesystem::path filePath, CConsoleHandler * const console);
 
 	/// Configures the logging system by parsing the logging settings. It adds the console target and the file target to the global logger.
 	/// Doesn't throw, but logs on success or fault.
@@ -30,12 +30,15 @@ public:
 
 	/// Configures a default logging system by adding the console target and the file target to the global logger.
 	void configureDefault();
-
 private:
-	ELogLevel::ELogLevel getLogLevel(const std::string & level) const;
-	EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName) const;
-
-	std::string filePath;
+	// Gets ELogLevel enum from string. (Should be moved to CLogger as a separate function?)
+	// Throws: std::runtime_error
+	static ELogLevel::ELogLevel getLogLevel(const std::string & level);
+	// Gets EConsoleTextColor enum from strings. (Should be moved to CLogger as a separate function?)
+	// Throws: std::runtime_error
+	static EConsoleTextColor::EConsoleTextColor getConsoleColor(const std::string & colorName);
+
+	boost::filesystem::path filePath;
 	CConsoleHandler * console;
 	bool appendToLogFile;
 };

+ 93 - 221
lib/logging/CLogger.cpp

@@ -1,46 +1,51 @@
-#ifdef __ANDROID__
-#include <android/log.h>
-#endif
-
 #include "StdInc.h"
 #include "CLogger.h"
 
+#ifdef VCMI_ANDROID
+#include <android/log.h>
+
+namespace ELogLevel
+{
+	int toAndroid(ELogLevel logLevel)
+	{
+		switch (logLevel)
+		{
+			case TRACE: return ANDROID_LOG_VERBOSE;
+			case DEBUG: return ANDROID_LOG_DEBUG;
+			case INFO:  return ANDROID_LOG_INFO;
+			case WARN:  return ANDROID_LOG_WARN;
+			case ERROR: return ANDROID_LOG_ERROR;
+			default:;
+		}
+		return ANDROID_LOG_UNKNOWN;
+	}
+}
+#endif
+
 const std::string CLoggerDomain::DOMAIN_GLOBAL = "global";
 
-CLoggerDomain::CLoggerDomain(const std::string & name) : name(name)
+CLoggerDomain::CLoggerDomain(std::string name) : name(std::move(name))
 {
-	if(name.empty()) throw std::runtime_error("Logger domain cannot be empty.");
+	if (this->name.empty())
+		throw std::runtime_error("Logger domain cannot be empty.");
 }
 
 CLoggerDomain CLoggerDomain::getParent() const
 {
-	if(isGlobalDomain()) return *this;
+	if(isGlobalDomain())
+		return *this;
 
-	size_t pos = name.find_last_of(".");
+	const size_t pos = name.find_last_of(".");
 	if(pos != std::string::npos)
-	{
 		return CLoggerDomain(name.substr(0, pos));
-	}
-	else
-	{
-		return CLoggerDomain(DOMAIN_GLOBAL);
-	}
+	return CLoggerDomain(DOMAIN_GLOBAL);
 }
 
-bool CLoggerDomain::isGlobalDomain() const
-{
-	return name == DOMAIN_GLOBAL;
-}
+bool CLoggerDomain::isGlobalDomain() const { return name == DOMAIN_GLOBAL; }
 
-std::string CLoggerDomain::getName() const
-{
-	return name;
-}
+const std::string& CLoggerDomain::getName() const { return name; }
 
-CLoggerStream::CLoggerStream(const CLogger & logger, ELogLevel::ELogLevel level) : logger(logger), level(level), sbuffer(nullptr)
-{
-
-}
+CLoggerStream::CLoggerStream(const CLogger & logger, ELogLevel::ELogLevel level) : logger(logger), level(level), sbuffer(nullptr) {}
 
 CLoggerStream::~CLoggerStream()
 {
@@ -67,20 +72,14 @@ CLogger * CLogger::getLogger(const CLoggerDomain & domain)
 	TLockGuardRec _(smx);
 
 	CLogger * logger = CLogManager::get().getLogger(domain);
-	if(logger)
-	{
-		return logger;
-	}
-	else
+	if(!logger) // Create new logger
 	{
 		logger = new CLogger(domain);
 		if(domain.isGlobalDomain())
-		{
 			logger->setLevel(ELogLevel::TRACE);
-		}
 		CLogManager::get().addLogger(logger);
-		return logger;
 	}
+	return logger;
 }
 
 CLogger * CLogger::getGlobalLogger()
@@ -102,62 +101,22 @@ CLogger::CLogger(const CLoggerDomain & domain) : domain(domain)
 	}
 }
 
-void CLogger::trace(const std::string & message) const
-{
-	log(ELogLevel::TRACE, message);
-}
-
-CLoggerStream CLogger::traceStream() const
-{
-	return CLoggerStream(*this, ELogLevel::TRACE);
-}
-
-void CLogger::debug(const std::string & message) const
-{
-	log(ELogLevel::DEBUG, message);
-}
-
-CLoggerStream CLogger::debugStream() const
-{
-	return CLoggerStream(*this, ELogLevel::DEBUG);
-}
-
-void CLogger::info(const std::string & message) const
-{
-	log(ELogLevel::INFO, message);
-}
-
-CLoggerStream CLogger::infoStream() const
-{
-	return CLoggerStream(*this, ELogLevel::INFO);
-}
+void CLogger::trace(const std::string & message) const { log(ELogLevel::TRACE, message); }
+void CLogger::debug(const std::string & message) const { log(ELogLevel::DEBUG, message); }
+void CLogger::info(const std::string & message) const { log(ELogLevel::INFO, message); }
+void CLogger::warn(const std::string & message) const { log(ELogLevel::WARN, message); }
+void CLogger::error(const std::string & message) const { log(ELogLevel::ERROR, message); }
 
-void CLogger::warn(const std::string & message) const
-{
-	log(ELogLevel::WARN, message);
-}
-
-CLoggerStream CLogger::warnStream() const
-{
-	return CLoggerStream(*this, ELogLevel::WARN);
-}
-
-void CLogger::error(const std::string & message) const
-{
-	log(ELogLevel::ERROR, message);
-}
-
-CLoggerStream CLogger::errorStream() const
-{
-	return CLoggerStream(*this, ELogLevel::ERROR);
-}
+CLoggerStream CLogger::traceStream() const { return CLoggerStream(*this, ELogLevel::TRACE); }
+CLoggerStream CLogger::debugStream() const { return CLoggerStream(*this, ELogLevel::DEBUG); }
+CLoggerStream CLogger::infoStream() const { return CLoggerStream(*this, ELogLevel::INFO); }
+CLoggerStream CLogger::warnStream() const { return CLoggerStream(*this, ELogLevel::WARN); }
+CLoggerStream CLogger::errorStream() const { return CLoggerStream(*this, ELogLevel::ERROR); }
 
 void CLogger::log(ELogLevel::ELogLevel level, const std::string & message) const
 {
 	if(getEffectiveLevel() <= level)
-	{
 		callTargets(LogRecord(domain, level, message));
-	}
 }
 
 ELogLevel::ELogLevel CLogger::getLevel() const
@@ -169,14 +128,11 @@ ELogLevel::ELogLevel CLogger::getLevel() const
 void CLogger::setLevel(ELogLevel::ELogLevel level)
 {
 	TLockGuard _(mx);
-	if(domain.isGlobalDomain() && level == ELogLevel::NOT_SET) return;
-	this->level = level;
+	if (!domain.isGlobalDomain() || level != ELogLevel::NOT_SET)
+		this->level = level;
 }
 
-const CLoggerDomain & CLogger::getDomain() const
-{
-	return domain;
-}
+const CLoggerDomain & CLogger::getDomain() const { return domain; }
 
 void CLogger::addTarget(unique_ptr<ILogTarget> && target)
 {
@@ -187,9 +143,8 @@ void CLogger::addTarget(unique_ptr<ILogTarget> && target)
 ELogLevel::ELogLevel CLogger::getEffectiveLevel() const
 {
 	for(const CLogger * logger = this; logger != nullptr; logger = logger->parent)
-	{
-		if(logger->getLevel() != ELogLevel::NOT_SET) return logger->getLevel();
-	}
+		if(logger->getLevel() != ELogLevel::NOT_SET)
+			return logger->getLevel();
 
 	// This shouldn't be reached, as the root logger must have set a log level
 	return ELogLevel::INFO;
@@ -199,12 +154,8 @@ void CLogger::callTargets(const LogRecord & record) const
 {
 	TLockGuard _(mx);
 	for(const CLogger * logger = this; logger != nullptr; logger = logger->parent)
-	{
 		for(auto & target : logger->targets)
-		{
 			target->write(record);
-		}
-	}
 }
 
 void CLogger::clearTargets()
@@ -213,26 +164,15 @@ void CLogger::clearTargets()
 	targets.clear();
 }
 
-bool CLogger::isDebugEnabled() const
-{
-	return getEffectiveLevel() <= ELogLevel::DEBUG;
-}
-
-bool CLogger::isTraceEnabled() const
-{
-	return getEffectiveLevel() <= ELogLevel::TRACE;
-}
+bool CLogger::isDebugEnabled() const { return getEffectiveLevel() <= ELogLevel::DEBUG; }
+bool CLogger::isTraceEnabled() const { return getEffectiveLevel() <= ELogLevel::TRACE; }
 
 CTraceLogger::CTraceLogger(const CLogger * logger, const std::string & beginMessage, const std::string & endMessage)
 	: logger(logger), endMessage(endMessage)
 {
-	logger->traceStream() << beginMessage;
-}
-
-CTraceLogger::~CTraceLogger()
-{
-	logger->traceStream() << endMessage;
+	logger->trace(beginMessage);
 }
+CTraceLogger::~CTraceLogger() { logger->trace(std::move(endMessage)); }
 
 CLogManager & CLogManager::get()
 {
@@ -241,17 +181,11 @@ CLogManager & CLogManager::get()
 	return instance;
 }
 
-CLogManager::CLogManager()
-{
-
-}
-
+CLogManager::CLogManager() { }
 CLogManager::~CLogManager()
 {
 	for(auto & i : loggers)
-	{
 		delete i.second;
-	}
 }
 
 void CLogManager::addLogger(CLogger * logger)
@@ -265,34 +199,30 @@ CLogger * CLogManager::getLogger(const CLoggerDomain & domain)
 	TLockGuard _(mx);
 	auto it = loggers.find(domain.getName());
 	if(it != loggers.end())
-	{
 		return it->second;
-	}
 	else
-	{
 		return nullptr;
-	}
 }
 
-CLogFormatter::CLogFormatter() : pattern("%m")
+CLogFormatter::CLogFormatter() : CLogFormatter("%m") { }
+
+CLogFormatter::CLogFormatter(const std::string & pattern) : pattern(pattern)
 {
 	boost::posix_time::time_facet * facet = new boost::posix_time::time_facet("%H:%M:%S");
 	dateStream.imbue(std::locale(dateStream.getloc(), facet));
 }
 
-CLogFormatter::CLogFormatter(const std::string & pattern)
-{
-	setPattern(pattern);
-}
+CLogFormatter::CLogFormatter(const CLogFormatter & c) : pattern(c.pattern) { }
+CLogFormatter::CLogFormatter(CLogFormatter && m) : pattern(std::move(m.pattern)) { }
 
-CLogFormatter::CLogFormatter(const CLogFormatter & other)
+CLogFormatter & CLogFormatter::operator=(const CLogFormatter & c)
 {
-	*this = other;
+	pattern = c.pattern;
+	return *this;
 }
-
-CLogFormatter & CLogFormatter::operator=(const CLogFormatter & other)
+CLogFormatter & CLogFormatter::operator=(CLogFormatter && m)
 {
-	pattern = other.pattern;
+	pattern = std::move(m.pattern);
 	return *this;
 }
 
@@ -336,15 +266,10 @@ std::string CLogFormatter::format(const LogRecord & record) const
 	return message;
 }
 
-void CLogFormatter::setPattern(const std::string & pattern)
-{
-	this->pattern = pattern;
-}
+void CLogFormatter::setPattern(const std::string & pattern) { this->pattern = pattern; }
+void CLogFormatter::setPattern(std::string && pattern) { this->pattern = std::move(pattern); }
 
-const std::string & CLogFormatter::getPattern() const
-{
-	return pattern;
-}
+const std::string & CLogFormatter::getPattern() const { return pattern; }
 
 CColorMapping::CColorMapping()
 {
@@ -365,30 +290,24 @@ void CColorMapping::setColorFor(const CLoggerDomain & domain, ELogLevel::ELogLev
 
 EConsoleTextColor::EConsoleTextColor CColorMapping::getColorFor(const CLoggerDomain & domain, ELogLevel::ELogLevel level) const
 {
-	std::string name = domain.getName();
+	CLoggerDomain currentDomain = domain;
 	while(true)
 	{
-		const auto & loggerPair = map.find(name);
+		const auto & loggerPair = map.find(currentDomain.getName());
 		if(loggerPair != map.end())
 		{
 			const auto & levelMap = loggerPair->second;
 			const auto & levelPair = levelMap.find(level);
 			if(levelPair != levelMap.end())
-			{
 				return levelPair->second;
-			}
 		}
 
-		CLoggerDomain currentDomain(name);
-		if(!currentDomain.isGlobalDomain())
-		{
-			name = currentDomain.getParent().getName();
-		}
-		else
-		{
+		if (currentDomain.isGlobalDomain())
 			break;
-		}
+
+		currentDomain = currentDomain.getParent();
 	}
+
 	throw std::runtime_error("failed to find color for requested domain/level pair");
 }
 
@@ -399,103 +318,56 @@ CLogConsoleTarget::CLogConsoleTarget(CConsoleHandler * console) : console(consol
 
 void CLogConsoleTarget::write(const LogRecord & record)
 {
-	if(threshold > record.level) return;
+	if(threshold > record.level)
+		return;
 
 	std::string message = formatter.format(record);
 
-#ifdef __ANDROID__
-	__android_log_print(ANDROID_LOG_INFO, "VCMI", "%s", message.c_str());
+#ifdef VCMI_ANDROID
+	__android_log_write(ELogLevel::toAndroid(record.level), "VCMI", message.c_str());
 #endif
 
-	bool printToStdErr = record.level >= ELogLevel::WARN;
+	const bool printToStdErr = record.level >= ELogLevel::WARN;
 	if(console)
 	{
-		if(coloredOutputEnabled)
-		{
-			console->print(message, true, colorMapping.getColorFor(record.domain, record.level));
-		}
-		else
-		{
-			console->print(message, true, EConsoleTextColor::DEFAULT, printToStdErr);
-		}
+		const EConsoleTextColor::EConsoleTextColor textColor =
+			coloredOutputEnabled ? colorMapping.getColorFor(record.domain, record.level) : EConsoleTextColor::DEFAULT;
+		
+		console->print(message, true, textColor, printToStdErr);
 	}
 	else
 	{
 		TLockGuard _(mx);
 		if(printToStdErr)
-		{
 			std::cerr << message << std::endl;
-		}
 		else
-		{
 			std::cout << message << std::endl;
-		}
 	}
 }
 
-bool CLogConsoleTarget::isColoredOutputEnabled() const
-{
-	return coloredOutputEnabled;
-}
-
-void CLogConsoleTarget::setColoredOutputEnabled(bool coloredOutputEnabled)
-{
-	this->coloredOutputEnabled = coloredOutputEnabled;
-}
-
-ELogLevel::ELogLevel CLogConsoleTarget::getThreshold() const
-{
-	return threshold;
-}
-
-void CLogConsoleTarget::setThreshold(ELogLevel::ELogLevel threshold)
-{
-	this->threshold = threshold;
-}
-
-const CLogFormatter & CLogConsoleTarget::getFormatter() const
-{
-	return formatter;
-}
+bool CLogConsoleTarget::isColoredOutputEnabled() const { return coloredOutputEnabled; }
+void CLogConsoleTarget::setColoredOutputEnabled(bool coloredOutputEnabled) { this->coloredOutputEnabled = coloredOutputEnabled; }
 
-void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter)
-{
-	this->formatter = formatter;
-}
+ELogLevel::ELogLevel CLogConsoleTarget::getThreshold() const { return threshold; }
+void CLogConsoleTarget::setThreshold(ELogLevel::ELogLevel threshold) { this->threshold = threshold; }
 
-const CColorMapping & CLogConsoleTarget::getColorMapping() const
-{
-	return colorMapping;
-}
+const CLogFormatter & CLogConsoleTarget::getFormatter() const { return formatter; }
+void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter) { this->formatter = formatter; }
 
-void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping)
-{
-	this->colorMapping = colorMapping;
-}
+const CColorMapping & CLogConsoleTarget::getColorMapping() const { return colorMapping; }
+void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping) { this->colorMapping = colorMapping; }
 
-CLogFileTarget::CLogFileTarget(const std::string & filePath, bool append /*= true*/)
-	: file(filePath, append ? std::ios_base::app : std::ios_base::out)
+CLogFileTarget::CLogFileTarget(boost::filesystem::path filePath, bool append /*= true*/)
+	: file(std::move(filePath), append ? std::ios_base::app : std::ios_base::out)
 {
 	formatter.setPattern("%d %l %n [%t] - %m");
 }
 
-CLogFileTarget::~CLogFileTarget()
-{
-	file.close();
-}
-
 void CLogFileTarget::write(const LogRecord & record)
 {
 	TLockGuard _(mx);
 	file << formatter.format(record) << std::endl;
 }
 
-const CLogFormatter & CLogFileTarget::getFormatter() const
-{
-	return formatter;
-}
-
-void CLogFileTarget::setFormatter(const CLogFormatter & formatter)
-{
-	this->formatter = formatter;
-}
+const CLogFormatter & CLogFileTarget::getFormatter() const { return formatter; }
+void CLogFileTarget::setFormatter(const CLogFormatter & formatter) { this->formatter = formatter; }

+ 36 - 30
lib/logging/CLogger.h

@@ -19,15 +19,19 @@ class ILogTarget;
 
 namespace ELogLevel
 {
-enum ELogLevel
-{
-	NOT_SET = 0,
-	TRACE,
-	DEBUG,
-	INFO,
-	WARN,
-	ERROR
-};
+	enum ELogLevel
+	{
+		NOT_SET = 0,
+		TRACE,
+		DEBUG,
+		INFO,
+		WARN,
+		ERROR
+	};
+
+	#ifdef VCMI_ANDROID
+		int toAndroid(ELogLevel logLevel);
+	#endif
 }
 
 /// The class CLoggerDomain provides convenient access to super domains from a sub domain.
@@ -36,9 +40,9 @@ class DLL_LINKAGE CLoggerDomain
 public:
 	/// Constructs a CLoggerDomain with the domain designated by name.
 	/// Sub-domains can be specified by separating domains by a dot, e.g. "ai.battle". The global domain is named "global".
-	explicit CLoggerDomain(const std::string & name);
+	explicit CLoggerDomain(std::string name);
 
-	std::string getName() const;
+	const std::string& getName() const;
 	CLoggerDomain getParent() const;
 	bool isGlobalDomain() const;
 
@@ -58,8 +62,11 @@ public:
 	template<typename T>
 	CLoggerStream & operator<<(const T & data)
 	{
-		if(!sbuffer) sbuffer = new std::stringstream();
+		if(!sbuffer)
+			sbuffer = new std::stringstream(std::ios_base::out);
+
 		(*sbuffer) << data;
+
 		return *this;
 	}
 
@@ -84,18 +91,16 @@ public:
 
 	/// Log methods for various log levels
 	void trace(const std::string & message) const;
-	CLoggerStream traceStream() const;
-
 	void debug(const std::string & message) const;
-	CLoggerStream debugStream() const;
-
 	void info(const std::string & message) const;
-	CLoggerStream infoStream() const;
-
 	void warn(const std::string & message) const;
-	CLoggerStream warnStream() const;
-
 	void error(const std::string & message) const;
+
+	/// Log streams for various log levels
+	CLoggerStream traceStream() const;
+	CLoggerStream debugStream() const;
+	CLoggerStream infoStream() const;
+	CLoggerStream warnStream() const;
 	CLoggerStream errorStream() const;
 
 	inline void log(ELogLevel::ELogLevel level, const std::string & message) const;
@@ -184,10 +189,7 @@ struct DLL_LINKAGE LogRecord
 {
 	LogRecord(const CLoggerDomain & domain, ELogLevel::ELogLevel level, const std::string & message)
 		: domain(domain), level(level), message(message), timeStamp(boost::posix_time::second_clock::local_time()),
-		  threadId(boost::this_thread::get_id())
-	{
-
-	}
+		  threadId(boost::this_thread::get_id()) { }
 
 	CLoggerDomain domain;
 	ELogLevel::ELogLevel level;
@@ -208,12 +210,17 @@ class DLL_LINKAGE CLogFormatter
 {
 public:
 	CLogFormatter();
-	CLogFormatter(const std::string & pattern);
+	CLogFormatter(const CLogFormatter & copy);
+	CLogFormatter(CLogFormatter && move);
 
-	CLogFormatter(const CLogFormatter & other);
-	CLogFormatter & operator=(const CLogFormatter & other);
+	CLogFormatter(const std::string & pattern);
+	
+	CLogFormatter & operator=(const CLogFormatter & copy);
+	CLogFormatter & operator=(CLogFormatter && move);
 
 	void setPattern(const std::string & pattern);
+	void setPattern(std::string && pattern);
+
 	const std::string & getPattern() const;
 
 	std::string format(const LogRecord & record) const;
@@ -284,8 +291,7 @@ class DLL_LINKAGE CLogFileTarget : public ILogTarget
 public:
 	/// Constructs a CLogFileTarget and opens the file designated by filePath. If the append parameter is true, the file
 	/// will be appended to. Otherwise the file designated by filePath will be truncated before being opened.
-	explicit CLogFileTarget(const std::string & filePath, bool append = true);
-	~CLogFileTarget();
+	explicit CLogFileTarget(boost::filesystem::path filePath, bool append = true);
 
 	const CLogFormatter & getFormatter() const;
 	void setFormatter(const CLogFormatter & formatter);
@@ -293,7 +299,7 @@ public:
 	void write(const LogRecord & record) override;
 
 private:
-	std::ofstream file;
+	boost::filesystem::ofstream file;
 	CLogFormatter formatter;
 	mutable boost::mutex mx;
 };

+ 1 - 1
lib/mapObjects/CArmedInstance.h

@@ -13,7 +13,7 @@
  *
  */
 
-class BattleInfo;
+struct BattleInfo;
 class CGameState;
 
 class DLL_LINKAGE CArmedInstance: public CGObjectInstance, public CBonusSystemNode, public CCreatureSet

+ 1 - 1
lib/mapObjects/CBank.h

@@ -13,7 +13,7 @@
  *
  */
 
-class BankConfig;
+struct BankConfig;
 class CBankInstanceConstructor;
 
 class DLL_LINKAGE CBank : public CArmedInstance

+ 1 - 1
lib/mapObjects/CGHeroInstance.h

@@ -19,7 +19,7 @@
 class CHero;
 class CGBoat;
 class CGTownInstance;
-class TerrainTile;
+struct TerrainTile;
 
 class CGHeroPlaceholder : public CGObjectInstance
 {

+ 1 - 1
lib/mapObjects/CGPandoraBox.h

@@ -14,7 +14,7 @@
  *
  */
 
-class InfoWindow;
+struct InfoWindow;
 
 class DLL_LINKAGE CGPandoraBox : public CArmedInstance
 {

+ 1 - 1
lib/mapObjects/CObjectHandler.h

@@ -19,7 +19,7 @@
 class CGHeroInstance;
 class IGameCallback;
 class CGObjectInstance;
-class MetaString;
+struct MetaString;
 struct BattleResult;
 
 class DLL_LINKAGE IObjectInterface

+ 87 - 56
lib/rmg/float3.h

@@ -10,75 +10,103 @@
  *
  */
 
+// FIXME: Class doesn't contain three float values. Update name and description.
 /// Class which consists of three float values. Represents position virtual RMG (0;1) area.
 class float3
 {
 public:
 	float x, y;
 	si32 z;
-	inline float3():x(0),y(0),z(0){}; //c-tor, x/y/z initialized to 0
-	inline float3(const float X, const float Y, const si32 Z):x(X),y(Y),z(Z){}; //c-tor
-	inline float3(const float3 & val) : x(val.x), y(val.y), z(val.z){} //copy c-tor
-	inline float3 & operator=(const float3 & val) {x = val.x; y = val.y; z = val.z; return *this;} //assignemt operator
-	~float3() {} // d-tor - does nothing
-	inline float3 operator+(const float3 & i) const //returns float3 with coordinates increased by corresponding coordinate of given float3
-		{return float3(x+i.x,y+i.y,z+i.z);}
-	inline float3 operator+(const float i) const //returns float3 with coordinates increased by given numer
-		{return float3(x+i,y+i,z+i);}
-	inline float3 operator-(const float3 & i) const //returns float3 with coordinates decreased by corresponding coordinate of given float3
-		{return float3(x-i.x,y-i.y,z-i.z);}
-	inline float3 operator-(const float i) const //returns float3 with coordinates decreased by given numer
-		{return float3(x-i,y-i,z-i);}
-	inline float3 operator*(const float i) const //returns float3 with plane coordinates decreased by given numer
-		{return float3(x*i, y*i, z);}
-	inline float3 operator/(const float i) const //returns float3 with plane coordinates decreased by given numer
-		{return float3(x/i, y/i, z);}
-	inline float3 operator-() const //returns opposite position
-		{return float3(-x,-y,-z);}
-	inline double dist2d(const float3 &other) const //distance (z coord is not used)
-		{return std::sqrt((double)(x-other.x)*(x-other.x) + (y-other.y)*(y-other.y));}
-	inline bool areNeighbours(const float3 &other) const
-		{return dist2d(other) < 2. && z == other.z;}
-	inline void operator+=(const float3 & i)
+
+	float3() : x(0), y(0), z(0) {}
+	float3(const float X, const float Y, const si32 Z): x(X), y(Y), z(Z) {}
+	float3(const float3 & copy) : x(copy.x), y(copy.y), z(copy.z) {}
+	float3 & operator=(const float3 & copy) { x = copy.x; y = copy.y; z = copy.z; return *this; }
+
+	// returns float3 with coordinates increased by corresponding coordinate of given float3
+	float3 operator+(const float3 & i) const { return float3(x + i.x, y + i.y, z + i.z); }
+	// returns float3 with coordinates increased by given numer
+	float3 operator+(const float i) const { return float3(x + i, y + i, z + (si32)i); }
+	// returns float3 with coordinates decreased by corresponding coordinate of given float3
+	float3 operator-(const float3 & i) const { return float3(x - i.x, y - i.y, z - i.z); }
+	// returns float3 with coordinates decreased by given numer
+	float3 operator-(const float i) const { return float3(x - i, y - i, z - (si32)i); }
+	
+	// returns float3 with plane coordinates decreased by given numer
+	float3 operator*(const float i) const {return float3(x * i, y * i, z);}
+	// returns float3 with plane coordinates decreased by given numer
+	float3 operator/(const float i) const {return float3(x / i, y / i, z);}
+	
+	// returns opposite position
+	float3 operator-() const { return float3(-x, -y, -z); }
+	
+	// returns squared distance on Oxy plane (z coord is not used)
+	double dist2dSQ(const float3 & o) const
+	{
+		const double dx = (x - o.x);
+		const double dy = (y - o.y);
+		return dx*dx + dy*dy;
+	}
+	// returns distance on Oxy plane (z coord is not used)
+	double dist2d(const float3 &other) const { return std::sqrt(dist2dSQ(other)); }
+
+	bool areNeighbours(const float3 &other) const { return (dist2dSQ(other) < 4.0) && z == other.z; }
+	
+	float3& operator+=(const float3 & i)
 	{
-		x+=i.x;
-		y+=i.y;
-		z+=i.z;
+		x += i.x;
+		y += i.y;
+		z += i.z;
+
+		return *this;
 	}
-	inline void operator+=(const float & i)
+	float3& operator+=(const float & i)
 	{
-		x+=i;
-		y+=i;
-		z+=i;
+		x += i;
+		y += i;
+		z += (si32)i;
+
+		return *this;
 	}
-	inline void operator-=(const float3 & i)
+	
+	float3& operator-=(const float3 & i)
 	{
-		x-=i.x;
-		y-=i.y;
-		z-=i.z;
+		x -= i.x;
+		y -= i.y;
+		z -= i.z;
+
+		return *this;
 	}
-	inline void operator-=(const float & i)
+	float3& operator-=(const float & i)
 	{
-		x+=i;
-		y+=i;
-		z+=i;
+		x += i;
+		y += i;
+		z += (si32)i;
+
+		return *this;
 	}
-	inline void operator*=(const float & i) //scale on plane
+
+	// scale on plane
+	float3& operator*=(const float & i)
 	{
-		x*=i;
-		y*=i;
+		x *= i;
+		y *= i;
+
+		return *this;
 	}
-	inline void operator/=(const float & i) //scale on plane
+	// scale on plane
+	float3& operator/=(const float & i)
 	{
-		x/=i;
-		y/=i;
+		x /= i;
+		y /= i;
+
+		return *this;
 	}
 
-	inline bool operator==(const float3 & i) const
-		{return (x==i.x) && (y==i.y) && (z==i.z);}
-	inline bool operator!=(const float3 & i) const
-		{return !(*this==i);}
-	inline bool operator<(const float3 & i) const
+	bool operator==(const float3 & i) const { return (x == i.x) && (y == i.y) && (z == i.z); }
+	bool operator!=(const float3 & i) const { return (x != i.x) || (y != i.y) || (z != i.z); }
+	
+	bool operator<(const float3 & i) const
 	{
 		if (z<i.z)
 			return true;
@@ -92,32 +120,35 @@ public:
 			return true;
 		if (x>i.x)
 			return false;
+
 		return false;
 	}
-	inline std::string operator ()() const
+
+	std::string operator ()() const
 	{
 		return	"(" + boost::lexical_cast<std::string>(x) +
 				" " + boost::lexical_cast<std::string>(y) +
 				" " + boost::lexical_cast<std::string>(z) + ")";
 	}
-	inline bool valid() const
+
+	bool valid() const
 	{
 		return z >= 0; //minimal condition that needs to be fulfilled for tiles in the map
 	}
+
 	template <typename Handler> void serialize(Handler &h, const float version)
 	{
 		h & x & y & z;
 	}
-	
 };
+
 inline std::istream & operator>>(std::istream & str, float3 & dest)
 {
-	str>>dest.x>>dest.y>>dest.z;
-	return str;
+	return str >> dest.x >> dest.y >> dest.z;
 }
 inline std::ostream & operator<<(std::ostream & str, const float3 & sth)
 {
-	return str<<sth.x<<' '<<sth.y<<' '<<sth.z;
+	return str << sth.x << ' ' << sth.y << ' ' << sth.z;
 }
 
 struct Shashfloat3

+ 6 - 6
scripting/erm/ERMInterpreter.cpp

@@ -368,19 +368,19 @@ void ERMInterpreter::scanForScripts()
 {
 	using namespace boost::filesystem;
 	//parser checking
-	if(!exists(VCMIDirs::get().dataPaths().back() + "/Data/s/"))
+	const path dataPath = VCMIDirs::get().dataPaths().back() / "Data" / "s";
+	if(!exists(dataPath))
 	{
-		logGlobal->warnStream() << "Warning: Folder " << VCMIDirs::get().dataPaths().back() << "/Data/s/ doesn't exist!";
+		logGlobal->warnStream() << "Warning: Folder " << dataPath << " doesn't exist!";
 		return;
 	}
 	directory_iterator enddir;
-	for (directory_iterator dir(VCMIDirs::get().dataPaths().back() + "/Data/s"); dir!=enddir; dir++)
+	for (directory_iterator dir(dataPath); dir!=enddir; dir++)
 	{
 		if(is_regular(dir->status()))
 		{
-			std::string name = dir->path().leaf().string();
-			if( boost::algorithm::ends_with(name, ".erm") ||
-				boost::algorithm::ends_with(name, ".verm") )
+			const std::string ext = boost::to_upper_copy(dir->path().extension().string());
+			if (ext == ".ERM" || ext == ".VERM")
 			{
 				ERMParser ep(dir->path().string());
 				FileInfo * finfo = new FileInfo;

+ 8 - 8
server/CVCMIServer.cpp

@@ -18,7 +18,7 @@
 #include "CVCMIServer.h"
 #include "../lib/StartInfo.h"
 #include "../lib/mapping/CMap.h"
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 #include "../lib/Interprocess.h"
 #endif
 #include "../lib/VCMI_Lib.h"
@@ -32,7 +32,7 @@
 
 #include "../lib/UnlockGuard.h"
 
-#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
+#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
 #include <execinfo.h>
 #endif
 
@@ -41,7 +41,7 @@ std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX
 using namespace boost;
 using namespace boost::asio;
 using namespace boost::asio::ip;
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 namespace intpr = boost::interprocess;
 #endif
 bool end2 = false;
@@ -393,7 +393,7 @@ void CVCMIServer::newPregame()
 
 void CVCMIServer::start()
 {
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 	ServerReady *sr = nullptr;
 	intpr::mapped_region *mr;
 	try
@@ -416,7 +416,7 @@ void CVCMIServer::start()
     logNetwork->infoStream()<<"Listening for connections at port " << acceptor->local_endpoint().port();
 	auto  s = new tcp::socket(acceptor->get_io_service());
 	boost::thread acc(std::bind(vaccept,acceptor,s,&error));
-#ifndef __ANDROID__
+#ifndef VCMI_ANDROID
 	sr->setToTrueAndNotify();
 	delete mr;
 #endif
@@ -560,7 +560,7 @@ static void handleCommandOptions(int argc, char *argv[])
 	}
 }
 
-#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
+#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
 void handleLinuxSignal(int sig)
 {
 	const int STACKTRACE_SIZE = 100;
@@ -591,12 +591,12 @@ int main(int argc, char** argv)
 {
 	// Installs a sig sev segmentation violation handler
 	// to log stacktrace
-	#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(__ANDROID__)
+	#if defined(__GNUC__) && !defined (__MINGW32__) && !defined(VCMI_ANDROID)
 	signal(SIGSEGV, handleLinuxSignal);
 	#endif
 
 	console = new CConsoleHandler;
-	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() + "/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().userCachePath() + "/VCMI_Test_log.txt", console);
+	CBasicLogConfigurator logConfig(VCMIDirs::get().userCachePath() / "VCMI_Test_log.txt", console);
 	logConfig.configureDefault();
 	preinitDLL(console);
 	settings.init();