mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	VCMIDirs update #5
- Minor fixes - string based paths -> boost::filesystem::path paths (I hope it's final) - New user data path on windows - New moving dir method on windows.
This commit is contained in:
		
							
								
								
									
										13
									
								
								Global.h
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Global.h
									
									
									
									
									
								
							| @@ -96,7 +96,11 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| /* Commonly used C++, Boost headers */ | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| #ifndef VCMI_WINDOWS | ||||
| #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers | ||||
| #define NOMINMAX				// Exclude min/max macros from <Windows.h> | ||||
| #endif | ||||
|  | ||||
| #define _USE_MATH_DEFINES | ||||
|  | ||||
| #include <cstdio> | ||||
| @@ -148,6 +152,7 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size."); | ||||
| #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> | ||||
| @@ -158,10 +163,6 @@ 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 VCMI_ANDROID | ||||
| 	#include <android/log.h> | ||||
| #endif | ||||
|  | ||||
| #ifndef M_PI | ||||
| 	#define M_PI 3.14159265358979323846 | ||||
| #endif | ||||
| @@ -196,7 +197,7 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec; | ||||
| /* Macros */ | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| // Import + Export macro declarations | ||||
| #ifdef _WIN32 | ||||
| #ifdef VCMI_WINDOWS | ||||
| #  ifdef __GNUC__ | ||||
| #    define DLL_EXPORT __attribute__((dllexport)) | ||||
| #  else | ||||
| @@ -210,7 +211,7 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec; | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #ifdef VCMI_WINDOWS | ||||
| #  ifdef __GNUC__ | ||||
| #    define DLL_IMPORT __attribute__((dllimport)) | ||||
| #  else | ||||
|   | ||||
| @@ -1601,22 +1601,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); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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"); | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -35,7 +35,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,7 +45,13 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName) | ||||
| 	TGetAIFun getAI = nullptr; | ||||
| 	TGetNameFun getName = nullptr; | ||||
|  | ||||
| #ifdef __ANDROID__ | ||||
| #ifndef VCMI_WINDOWS | ||||
| 	std::string dllname = libpath.string(); | ||||
| 		// I don't know other platforms. | ||||
| 		// Somebody should remove it soon. | ||||
| #endif | ||||
|  | ||||
| #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) { | ||||
| 		getName = (TGetNameFun)VCAI_GetAiName; | ||||
| @@ -61,12 +67,12 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName) | ||||
| 	} | ||||
| #else | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| 	HINSTANCE dll = LoadLibraryA(dllname.c_str()); | ||||
| #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); | ||||
| @@ -80,21 +86,20 @@ shared_ptr<rett> createAny(std::string dllname, std::string methodName) | ||||
| #endif | ||||
| 	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; | ||||
| @@ -113,9 +118,8 @@ shared_ptr<rett> createAnyAI(std::string dllname, std::string methodName) | ||||
|     logGlobal->infoStream() << "Opening " << dllname; | ||||
| 	const boost::filesystem::path filePath = | ||||
| 		VCMIDirs::get().libraryPath() / "AI" / VCMIDirs::get().libraryName(dllname); | ||||
| 	// TODO: createAny Should take boost::filesystem::path in argument. | ||||
| 	auto ret = createAny<rett>(filePath.string(), methodName); | ||||
| 	ret->dllName = dllname; | ||||
| 	auto ret = createAny<rett>(filePath, methodName); | ||||
| 	ret->dllName = std::move(dllname); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										171
									
								
								lib/VCMIDirs.cpp
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								lib/VCMIDirs.cpp
									
									
									
									
									
								
							| @@ -30,6 +30,75 @@ void IVCMIDirs::init() | ||||
| #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()) | ||||
| { | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| class VCMIDirsWIN32 : public IVCMIDirs | ||||
| { | ||||
| 	public: | ||||
| @@ -50,6 +119,9 @@ class VCMIDirsWIN32 : public IVCMIDirs | ||||
| 		std::string genHelpString() const override; | ||||
|  | ||||
| 		void init() override; | ||||
| 	protected: | ||||
| 		boost::filesystem::path oldUserDataPath() const; | ||||
| 		boost::filesystem::path oldUserSavePath() const; | ||||
| }; | ||||
|  | ||||
| void VCMIDirsWIN32::init() | ||||
| @@ -57,15 +129,21 @@ void VCMIDirsWIN32::init() | ||||
| 	// Call base (init dirs) | ||||
| 	IVCMIDirs::init(); | ||||
|  | ||||
| 	auto moveDirIfExists = [](const bfs::path& from, const bfs::path& to) | ||||
| 	// 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; // Nothing to do here. Flies away. | ||||
| 			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; // Nothing to do here. Flies away. | ||||
| 			return true; // Nothing to do here. Flies away. | ||||
| 		} | ||||
|  | ||||
| 		if (!bfs::is_directory(to)) | ||||
| @@ -103,23 +181,87 @@ void VCMIDirsWIN32::init() | ||||
| 		fileOp.lpszProgressTitle = nullptr; | ||||
|  | ||||
| 		const int errorCode = SHFileOperationW(&fileOp); | ||||
| 		if (errorCode != 0); // TODO: Log error. User should try to move files. | ||||
| 		else if (fileOp.fAnyOperationsAborted); // TODO: Log warn. User aborted operation. User should move files. | ||||
| 		else if (!bfs::is_empty(from)); // TODO: Log warn. Some files not moved. User should try to move files. | ||||
| 		else // TODO: Log fact that we moved files succefully. | ||||
| 			bfs::remove(from); | ||||
| 		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(); | ||||
| 	}; | ||||
|  | ||||
| 	moveDirIfExists(userDataPath() / "Games", userSavePath()); | ||||
| 	// 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()); | ||||
| } | ||||
|  | ||||
| bfs::path VCMIDirsWIN32::userDataPath() const | ||||
| { | ||||
| 	wchar_t profileDir[MAX_PATH]; | ||||
|  | ||||
| 	// The user's profile folder. A typical path is C:\Users\username. | ||||
| 	// FIXME: Applications should not create files or folders at this level; | ||||
| 	// they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. | ||||
| 	if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_MYDOCUMENTS, FALSE) != FALSE) | ||||
| 		return bfs::path(profileDir) / "My Games\\vcmi"; | ||||
| 	 | ||||
| 	return "."; | ||||
| } | ||||
|  | ||||
| bfs::path VCMIDirsWIN32::oldUserDataPath() const | ||||
| { | ||||
| 	wchar_t profileDir[MAX_PATH]; | ||||
| 	 | ||||
| 	if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_PROFILE, FALSE) == FALSE) // WinAPI way failed | ||||
| 	{ | ||||
| #if defined(_MSC_VER) && _MSC_VER >= 1700 | ||||
| @@ -145,6 +287,8 @@ bfs::path VCMIDirsWIN32::userDataPath() const | ||||
|  | ||||
| 	//return dataPaths()[0] ???; | ||||
| } | ||||
| bfs::path VCMIDirsWIN32::oldUserSavePath() const { return userDataPath() / "Games"; } | ||||
|  | ||||
| bfs::path VCMIDirsWIN32::userCachePath() const { return userDataPath(); } | ||||
| bfs::path VCMIDirsWIN32::userConfigPath() const { return userDataPath() / "config"; } | ||||
|  | ||||
| @@ -421,6 +565,9 @@ namespace VCMIDirs | ||||
| 		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; | ||||
| 		} | ||||
|   | ||||
| @@ -9,15 +9,6 @@ | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| // STL C++ | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| // Boost | ||||
| #include <boost/filesystem/path.hpp> | ||||
|  | ||||
| // TODO: File should be renamed to IVCMIDirs.h | ||||
|  | ||||
| class DLL_LINKAGE IVCMIDirs | ||||
| { | ||||
| 	public: | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -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(); | ||||
| 			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(); | ||||
|  | ||||
| 			fileList[ResourceID(mountPoint + filename, type)] = 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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -130,19 +130,18 @@ 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); | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	// TODO: CFilesystemLoader: Should take boost::filesystem::path in argument | ||||
| 	for (auto & path : VCMIDirs::get().dataPaths()) | ||||
| 	{ | ||||
| 		if (boost::filesystem::is_directory(path)) // some of system-provided paths may not exist | ||||
| 			initialLoader->addLoader(new CFilesystemLoader("", path.string(), 0, true), false); | ||||
| 			initialLoader->addLoader(new CFilesystemLoader("", path, 0, true), false); | ||||
| 	} | ||||
| 	initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath().string(), 0, true), false); | ||||
| 	initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath(), 0, true), false); | ||||
|  | ||||
| 	recurseInDir("CONFIG", 0);// look for configs | ||||
| 	recurseInDir("DATA", 0); // look for archives | ||||
| @@ -167,10 +166,9 @@ void CResourceHandler::initialize() | ||||
| 	//    |-saves | ||||
| 	//    |-config | ||||
|  | ||||
| 	// TODO: CFilesystemLoader should take boost::filesystem::path | ||||
| 	knownLoaders["root"] = new CFilesystemList(); | ||||
| 	knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath().string()); | ||||
| 	knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath().string()); | ||||
| 	knownLoaders["saves"] = new CFilesystemLoader("SAVES/", VCMIDirs::get().userSavePath()); | ||||
| 	knownLoaders["config"] = new CFilesystemLoader("CONFIG/", VCMIDirs::get().userConfigPath()); | ||||
|  | ||||
| 	auto localFS = new CFilesystemList(); | ||||
| 	localFS->addLoader(knownLoaders["saves"], true); | ||||
|   | ||||
| @@ -3,10 +3,7 @@ | ||||
|  | ||||
| #include "../CConfigHandler.h" | ||||
|  | ||||
| CBasicLogConfigurator::CBasicLogConfigurator(const boost::filesystem::path & filePath, CConsoleHandler * const console) : | ||||
| 	filePath(filePath), console(console), appendToLogFile(false) {} | ||||
|  | ||||
| CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path && filePath, CConsoleHandler * const console) : | ||||
| CBasicLogConfigurator::CBasicLogConfigurator(boost::filesystem::path filePath, CConsoleHandler * const console) : | ||||
| 	filePath(std::move(filePath)), console(console), appendToLogFile(false) {} | ||||
|  | ||||
| void CBasicLogConfigurator::configureDefault() | ||||
|   | ||||
| @@ -22,8 +22,7 @@ class JsonNode; | ||||
| class DLL_LINKAGE CBasicLogConfigurator | ||||
| { | ||||
| public: | ||||
| 	CBasicLogConfigurator(const boost::filesystem::path & filePath, CConsoleHandler * const console); | ||||
| 	CBasicLogConfigurator(boost::filesystem::path && filePath, CConsoleHandler * const 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. | ||||
|   | ||||
| @@ -1,14 +1,30 @@ | ||||
| #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) | ||||
| { | ||||
| 	if(name.empty()) | ||||
| 		throw std::runtime_error("Logger domain cannot be empty."); | ||||
| } | ||||
| CLoggerDomain::CLoggerDomain(std::string && name) : name(std::move(name)) | ||||
| CLoggerDomain::CLoggerDomain(std::string name) : name(std::move(name)) | ||||
| { | ||||
| 	if (this->name.empty()) | ||||
| 		throw std::runtime_error("Logger domain cannot be empty."); | ||||
| @@ -302,15 +318,16 @@ 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 VCMI_ANDROID | ||||
| 	__android_log_print(ANDROID_LOG_INFO, "VCMI", "%s", message.c_str()); | ||||
| 	__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) | ||||
| 	{ | ||||
| 		const EConsoleTextColor::EConsoleTextColor textColor = | ||||
| @@ -340,22 +357,12 @@ void CLogConsoleTarget::setFormatter(const CLogFormatter & formatter) { this->fo | ||||
| const CColorMapping & CLogConsoleTarget::getColorMapping() const { return colorMapping; } | ||||
| void CLogConsoleTarget::setColorMapping(const CColorMapping & colorMapping) { this->colorMapping = colorMapping; } | ||||
|  | ||||
| CLogFileTarget::CLogFileTarget(const boost::filesystem::path & filePath, bool append /*= true*/) | ||||
| 	: file(filePath, append ? std::ios_base::app : std::ios_base::out) | ||||
| { | ||||
| 	formatter.setPattern("%d %l %n [%t] - %m"); | ||||
| } | ||||
| CLogFileTarget::CLogFileTarget(boost::filesystem::path && filePath, bool append /*= true*/) | ||||
| 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); | ||||
|   | ||||
| @@ -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,8 +40,7 @@ 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); | ||||
| 	explicit CLoggerDomain(std::string name); | ||||
|  | ||||
| 	const std::string& getName() const; | ||||
| 	CLoggerDomain getParent() const; | ||||
| @@ -288,9 +291,7 @@ class DLL_LINKAGE CLogFileTarget : public ILogTarget | ||||
| public: | ||||
| 	/// Constructs a CLogFileTarget and opens the file designated by file_path. 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 boost::filesystem::path & file_path, bool append = true); | ||||
| 	explicit CLogFileTarget(boost::filesystem::path && file_path, bool append = true); | ||||
| 	~CLogFileTarget(); | ||||
| 	explicit CLogFileTarget(boost::filesystem::path file_path, bool append = true); | ||||
|  | ||||
| 	const CLogFormatter & getFormatter() const; | ||||
| 	void setFormatter(const CLogFormatter & formatter); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user