| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 | 
							- /*
 
-  * VCMIDirs.cpp, 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
 
-  *
 
-  */
 
- #include "StdInc.h"
 
- #include "VCMIDirs.h"
 
- #ifdef VCMI_IOS
 
- #include "iOS_utils.h"
 
- #elif defined(VCMI_ANDROID)
 
- #include "CAndroidVMHelper.h"
 
- #endif
 
- VCMI_LIB_NAMESPACE_BEGIN
 
- namespace bfs = boost::filesystem;
 
- bfs::path IVCMIDirs::userLogsPath() const { return userCachePath(); }
 
- bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
 
- bfs::path IVCMIDirs::userExtractedPath() const { return userCachePath() / "extracted"; }
 
- bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
 
- {
 
- 	return libraryPath() / desiredFolder / libraryName(baseLibName);
 
- }
 
- std::string IVCMIDirs::genHelpString() const
 
- {
 
- 	std::vector<std::string> tempVec;
 
- 	for (const bfs::path & path : dataPaths())
 
- 		tempVec.push_back(path.string());
 
- 	const auto 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 logs:		" + userLogsPath().string() + "\n"
 
- 		"  user saves:		" + userSavePath().string() + "\n"
 
- 		"  user extracted:	" + userExtractedPath().string() + "\n";
 
- }
 
- void IVCMIDirs::init()
 
- {
 
- 	// TODO: Log errors
 
- 	bfs::create_directories(userDataPath());
 
- 	bfs::create_directories(userCachePath());
 
- 	bfs::create_directories(userConfigPath());
 
- 	bfs::create_directories(userLogsPath());
 
- 	bfs::create_directories(userSavePath());
 
- }
 
- #ifdef VCMI_WINDOWS
 
- #ifdef __MINGW32__
 
-     #define _WIN32_IE 0x0500
 
- 	#ifndef CSIDL_MYDOCUMENTS
 
- 	#define CSIDL_MYDOCUMENTS CSIDL_PERSONAL
 
- 	#endif
 
- #endif // __MINGW32__
 
- #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())
 
- {
 
- 	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 final : public IVCMIDirs
 
- {
 
- 	public:
 
- 		bfs::path userDataPath() const override;
 
- 		bfs::path userCachePath() const override;
 
- 		bfs::path userConfigPath() const override;
 
- 		std::vector<bfs::path> dataPaths() const override;
 
- 		bfs::path clientPath() const override;
 
- 		bfs::path mapEditorPath() const override;
 
- 		bfs::path serverPath() const override;
 
- 		bfs::path libraryPath() const override;
 
- 		bfs::path binaryPath() const override;
 
- 		std::string libraryName(const std::string& basename) const override;
 
- 		void init() override;
 
- 	protected:
 
- 		bfs::path oldUserDataPath() const;
 
- 		bfs::path oldUserSavePath() const;
 
- };
 
- void VCMIDirsWIN32::init()
 
- {
 
- 	std::locale::global(boost::locale::generator().generate("en_US.UTF-8"));
 
- 	boost::filesystem::path::imbue(std::locale());
 
- 	// 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_directories(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());
 
- }
 
- 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 ".";
 
- }
 
- 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
 
- 		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";
 
- 	//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"; }
 
- std::vector<bfs::path> VCMIDirsWIN32::dataPaths() const
 
- {
 
- 	return std::vector<bfs::path>(1, bfs::path("."));
 
- }
 
- bfs::path VCMIDirsWIN32::clientPath() const { return binaryPath() / "VCMI_client.exe"; }
 
- bfs::path VCMIDirsWIN32::mapEditorPath() const { return binaryPath() / "VCMI_mapeditor.exe"; }
 
- bfs::path VCMIDirsWIN32::serverPath() const { return binaryPath() / "VCMI_server.exe"; }
 
- bfs::path VCMIDirsWIN32::libraryPath() const { return "."; }
 
- bfs::path VCMIDirsWIN32::binaryPath() const { return ".";  }
 
- std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { return basename + ".dll"; }
 
- #elif defined(VCMI_UNIX)
 
- class IVCMIDirsUNIX : public IVCMIDirs
 
- {
 
- 	public:
 
- 		bfs::path clientPath() const override;
 
- 		bfs::path mapEditorPath() const override;
 
- 		bfs::path serverPath() const override;
 
- 		virtual bool developmentMode() const;
 
- };
 
- bool IVCMIDirsUNIX::developmentMode() const
 
- {
 
- 	// We want to be able to run VCMI from single directory. E.g to run from build output directory
 
- 	const bool result = bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiclient");
 
- #if SINGLE_PROCESS_APP
 
- 	return result;
 
- #else
 
- 	return result && bfs::exists("vcmiserver");
 
- #endif
 
- }
 
- bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }
 
- bfs::path IVCMIDirsUNIX::mapEditorPath() const { return binaryPath() / "vcmieditor"; }
 
- bfs::path IVCMIDirsUNIX::serverPath() const { return binaryPath() / "vcmiserver"; }
 
- #ifdef VCMI_APPLE
 
- class VCMIDirsApple : public IVCMIDirsUNIX
 
- {
 
- public:
 
- 	bfs::path userConfigPath() const override;
 
- 	std::string libraryName(const std::string& basename) const override;
 
- };
 
- bfs::path VCMIDirsApple::userConfigPath() const { return userDataPath() / "config"; }
 
- std::string VCMIDirsApple::libraryName(const std::string& basename) const { return "lib" + basename + ".dylib"; }
 
- #ifdef VCMI_IOS
 
- class VCMIDirsIOS final : public VCMIDirsApple
 
- {
 
- public:
 
- 	bfs::path userDataPath() const override;
 
- 	bfs::path userCachePath() const override;
 
- 	bfs::path userLogsPath() const override;
 
- 	std::vector<bfs::path> dataPaths() const override;
 
- 	bfs::path libraryPath() const override;
 
- 	bfs::path fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const override;
 
- 	bfs::path binaryPath() const override;
 
- };
 
- bfs::path VCMIDirsIOS::userDataPath() const { return {iOS_utils::documentsPath()}; }
 
- bfs::path VCMIDirsIOS::userCachePath() const { return {iOS_utils::cachesPath()}; }
 
- bfs::path VCMIDirsIOS::userLogsPath() const { return {iOS_utils::documentsPath()}; }
 
- std::vector<bfs::path> VCMIDirsIOS::dataPaths() const
 
- {
 
- 	std::vector<bfs::path> paths;
 
- 	paths.reserve(4);
 
- #ifdef VCMI_IOS_SIM
 
- 	paths.emplace_back(iOS_utils::hostApplicationSupportPath());
 
- #endif
 
- 	paths.emplace_back(userDataPath());
 
- 	paths.emplace_back(iOS_utils::documentsPath());
 
- 	paths.emplace_back(binaryPath());
 
- 	return paths;
 
- }
 
- bfs::path VCMIDirsIOS::fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const
 
- {
 
- 	// iOS has flat libs directory structure
 
- 	return libraryPath() / libraryName(baseLibName);
 
- }
 
- bfs::path VCMIDirsIOS::libraryPath() const { return {iOS_utils::frameworksPath()}; }
 
- bfs::path VCMIDirsIOS::binaryPath() const { return {iOS_utils::bundlePath()}; }
 
- #elif defined(VCMI_MAC)
 
- class VCMIDirsOSX final : public VCMIDirsApple
 
- {
 
- public:
 
- 	bfs::path userDataPath() const override;
 
- 	bfs::path userCachePath() const override;
 
- 	bfs::path userLogsPath() const override;
 
- 	std::vector<bfs::path> dataPaths() const override;
 
- 	bfs::path libraryPath() const override;
 
- 	bfs::path binaryPath() const override;
 
- 	void init() override;
 
- };
 
- void VCMIDirsOSX::init()
 
- {
 
- 	// 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_directories(to);
 
- 		}
 
- 		for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file)
 
- 		{
 
- 			const bfs::path& srcFilePath = file->path();
 
- 			const bfs::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 (!bfs::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());
 
- }
 
- bfs::path VCMIDirsOSX::userDataPath() const
 
- {
 
- 	// This is Cocoa code that should be normally used to get path to Application Support folder but can't use it here for now...
 
- 	// NSArray* urls = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
 
- 	// 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::userLogsPath() const
 
- {
 
- 	// TODO: use proper objc code from Foundation framework
 
- 	if(const auto homeDir = std::getenv("HOME"))
 
- 		return bfs::path{homeDir} / "Library" / "Logs" / "vcmi";
 
- 	return IVCMIDirsUNIX::userLogsPath();
 
- }
 
- std::vector<bfs::path> VCMIDirsOSX::dataPaths() const
 
- {
 
- 	std::vector<bfs::path> ret;
 
- 	//FIXME: need some proper codepath for detecting running from build output directory
 
- 	if(developmentMode())
 
- 	{
 
- 		ret.push_back(".");
 
- 	}
 
- 	else
 
- 	{
 
- 		ret.push_back("../Resources/Data");
 
- 	}
 
- 	return ret;
 
- }
 
- bfs::path VCMIDirsOSX::libraryPath() const { return "."; }
 
- bfs::path VCMIDirsOSX::binaryPath() const { return "."; }
 
- #endif // VCMI_IOS, VCMI_MAC
 
- #elif defined(VCMI_ANDROID)
 
- class VCMIDirsAndroid : public IVCMIDirsUNIX
 
- {
 
- 	std::string basePath;
 
- 	std::string internalPath;
 
- 	std::string nativePath;
 
- public:
 
- 	std::string libraryName(const std::string & basename) const override;
 
- 	bfs::path fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const override;
 
- 	bfs::path binaryPath() const override;
 
- 	bfs::path libraryPath() const override;
 
- 	bfs::path userDataPath() const override;
 
- 	bfs::path userCachePath() const override;
 
- 	bfs::path userConfigPath() const override;
 
- 	std::vector<bfs::path> dataPaths() const override;
 
- 	void init() override;
 
- };
 
- std::string VCMIDirsAndroid::libraryName(const std::string & basename) const { return "lib" + basename + ".so"; }
 
- bfs::path VCMIDirsAndroid::binaryPath() const { return "."; }
 
- bfs::path VCMIDirsAndroid::libraryPath() const { return nativePath; }
 
- bfs::path VCMIDirsAndroid::userDataPath() const { return basePath; }
 
- bfs::path VCMIDirsAndroid::userCachePath() const { return userDataPath() / "cache"; }
 
- bfs::path VCMIDirsAndroid::userConfigPath() const { return userDataPath() / "config"; }
 
- bfs::path VCMIDirsAndroid::fullLibraryPath(const std::string & desiredFolder, const std::string & baseLibName) const
 
- {
 
- 	// ignore passed folder (all libraries in android are dumped into a single folder)
 
- 	return libraryPath() / libraryName(baseLibName);
 
- }
 
- std::vector<bfs::path> VCMIDirsAndroid::dataPaths() const
 
- {
 
- 	return {
 
- 		internalPath,
 
- 		userDataPath(),
 
- 	};
 
- }
 
- void VCMIDirsAndroid::init()
 
- {
 
- 	// asks java code to retrieve needed paths from environment
 
- 	CAndroidVMHelper envHelper;
 
- 	basePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "dataRoot");
 
- 	internalPath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "internalDataRoot");
 
- 	nativePath = envHelper.callStaticStringMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "nativePath");
 
- 	IVCMIDirsUNIX::init();
 
- }
 
- #elif defined(VCMI_XDG)
 
- class VCMIDirsXDG : public IVCMIDirsUNIX
 
- {
 
- public:
 
- 	bfs::path userDataPath() const override;
 
- 	bfs::path userCachePath() const override;
 
- 	bfs::path userConfigPath() const override;
 
- 	std::vector<bfs::path> dataPaths() const override;
 
- 	bfs::path libraryPath() const override;
 
- 	bfs::path binaryPath() const override;
 
- 	std::string libraryName(const std::string& basename) const override;
 
- };
 
- bfs::path VCMIDirsXDG::userDataPath() const
 
- {
 
- 	// $XDG_DATA_HOME, default: $HOME/.local/share
 
- 	const char* homeDir;
 
- 	if((homeDir = getenv("XDG_DATA_HOME")))
 
- 		return bfs::path(homeDir) / "vcmi";
 
- 	else if((homeDir = getenv("HOME")))
 
- 		return bfs::path(homeDir) / ".local" / "share" / "vcmi";
 
- 	else
 
- 		return ".";
 
- }
 
- bfs::path VCMIDirsXDG::userCachePath() const
 
- {
 
- 	// $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 ".";
 
- }
 
- bfs::path VCMIDirsXDG::userConfigPath() const
 
- {
 
- 	// $XDG_CONFIG_HOME, default: $HOME/.config
 
- 	const char * tempResult = getenv("XDG_CONFIG_HOME");
 
- 	if (tempResult)
 
- 		return bfs::path(tempResult) / "vcmi";
 
- 	
 
- 	tempResult = getenv("HOME");
 
- 	if (tempResult)
 
- 		return bfs::path(tempResult) / ".config" / "vcmi";
 
- 	return ".";
 
- }
 
- std::vector<bfs::path> VCMIDirsXDG::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;
 
- 	if(developmentMode())
 
- 	{
 
- 		//For now we'll disable usage of system directories when VCMI running from bin directory
 
- 		ret.emplace_back(".");
 
- 	}
 
- 	else
 
- 	{
 
- 		ret.emplace_back(M_DATA_DIR);
 
- 		const char * tempResult;
 
- 		if((tempResult = getenv("XDG_DATA_DIRS")) != nullptr)
 
- 		{
 
- 			std::string dataDirsEnv = tempResult;
 
- 			std::vector<std::string> dataDirs;
 
- 			boost::split(dataDirs, dataDirsEnv, boost::is_any_of(":"));
 
- 			for (auto & entry : boost::adaptors::reverse(dataDirs))
 
- 				ret.push_back(bfs::path(entry) / "vcmi");
 
- 		}
 
- 		else
 
- 		{
 
- 			ret.push_back(bfs::path("/usr/share") / "vcmi");
 
- 			ret.push_back(bfs::path("/usr/local/share") / "vcmi");
 
- 		}
 
- 		// Debian and other distributions might want to use it while it's not part of XDG
 
- 		ret.push_back(bfs::path("/usr/share/games") / "vcmi");
 
- 	}
 
- 	return ret;
 
- }
 
- bfs::path VCMIDirsXDG::libraryPath() const
 
- {
 
- 	if(developmentMode())
 
- 		return ".";
 
- 	else
 
- 		return M_LIB_DIR;
 
- }
 
- bfs::path VCMIDirsXDG::binaryPath() const
 
- {
 
- 	if(developmentMode())
 
- 		return ".";
 
- 	else
 
- 		return M_BIN_DIR;
 
- }
 
- std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
 
- #endif // VCMI_APPLE, VCMI_ANDROID, VCMI_XDG
 
- #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_XDG)
 
- 			static VCMIDirsXDG singleton;
 
- 		#elif defined(VCMI_MAC)
 
- 			static VCMIDirsOSX singleton;
 
- 		#elif defined(VCMI_IOS)
 
- 			static VCMIDirsIOS singleton;
 
- 		#endif
 
- 		static std::once_flag flag;
 
- 		std::call_once(flag, [] { singleton.init(); });
 
- 		return singleton;
 
- 	}
 
- }
 
- VCMI_LIB_NAMESPACE_END
 
 
  |