Browse Source

- (linux) XDG filesystem support

BIG NOTE TO LINUX USERS
All user data has been moved according to XDG specs:
- Game data (H3 files and mods) and saves: from ~/.vcmi to ~/.local/
- Temporary files, including logs: from ~/.vcmi to ~/.cache/vcmi
- Config files: from ~/.vcmi/config to ~/.config/vcmi

For compatibility VCMI will read game data from ~/.vcmi as well but this
is temporary behavior and will be removed
Ivan Savenko 11 years ago
parent
commit
5cbec833c2
7 changed files with 100 additions and 29 deletions
  1. 1 1
      README.linux
  2. 8 3
      client/CMT.cpp
  3. 2 1
      launcher/launcherdirs.cpp
  4. 78 16
      lib/VCMIDirs.cpp
  5. 0 3
      lib/VCMIDirs.h
  6. 5 4
      lib/filesystem/Filesystem.cpp
  7. 6 1
      vcmibuilder

+ 1 - 1
README.linux

@@ -28,7 +28,7 @@ To compile, the following packages (and their development counterparts) are need
 		- locale
 
 On Debian-based systems (e.g. Ubuntu) run:
-  sudo apt-get install cmake g++ libsdl1.2debian libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev
+  sudo apt-get install cmake g++ libsdl1.2debian libsdl-image1.2-dev libsdl-ttf2.0-dev libsdl-mixer1.2-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev qtbase5-dev
 
 On RPM-based distributions (e.g. Fedora) run:
   sudo yum install cmake gcc-c++ SDL-devel SDL_image-devel SDL_ttf-devel SDL_mixer-devel boost boost-devel boost-filesystem boost-system boost-thread boost-program-options boost-locale zlib-devel ffmpeg-devel ffmpeg-libs

+ 8 - 3
client/CMT.cpp

@@ -171,9 +171,14 @@ void init()
 static void prog_version(void)
 {
 	printf("%s\n", GameConstants::VCMI_VERSION.c_str());
-	printf("  data directory:    %s\n", VCMIDirs::get().dataPaths().back().c_str());
-	printf("  library directory: %s\n", VCMIDirs::get().libraryPath().c_str());
-	printf("  path to server:    %s\n", VCMIDirs::get().serverPath().c_str());
+	printf("  game data:   %s\n", boost::algorithm::join(VCMIDirs::get().dataPaths(), ":").c_str());
+	printf("  libraries:   %s\n", VCMIDirs::get().libraryPath().c_str());
+	printf("  server:      %s\n", VCMIDirs::get().serverPath().c_str());
+	printf("\n");
+	printf("  user data:   %s\n", VCMIDirs::get().userDataPath().c_str());
+	printf("  user cache:  %s\n", VCMIDirs::get().userCachePath().c_str());
+	printf("  user config: %s\n", VCMIDirs::get().userConfigPath().c_str());
+	printf("  user saves:  %s\n", VCMIDirs::get().userSavePath().c_str());
 }
 
 static void prog_help(const po::options_description &opts)

+ 2 - 1
launcher/launcherdirs.cpp

@@ -8,6 +8,7 @@ static CLauncherDirs launcherDirsGlobal;
 CLauncherDirs::CLauncherDirs()
 {
 	QDir().mkdir(downloadsPath());
+	QDir().mkdir(modsPath());
 }
 
 CLauncherDirs & CLauncherDirs::get()
@@ -22,5 +23,5 @@ QString CLauncherDirs::downloadsPath()
 
 QString CLauncherDirs::modsPath()
 {
-	return QString::fromUtf8(VCMIDirs::get().userCachePath().c_str()) + "/Mods";
+	return QString::fromUtf8(VCMIDirs::get().userDataPath().c_str()) + "/Mods";
 }

+ 78 - 16
lib/VCMIDirs.cpp

@@ -27,6 +27,9 @@ VCMIDirs & VCMIDirs::get()
 	return VCMIDirsGlobal;
 }
 
+//FIXME: find way to at least decrease size of this ifdef (along with cleanup in CMake)
+#if defined(_WIN32)
+
 std::string VCMIDirs::userCachePath() const
 {
 	return userDataPath();
@@ -42,14 +45,6 @@ std::string VCMIDirs::userSavePath() const
 	return userDataPath() + "/Games";
 }
 
-std::vector<std::string> VCMIDirs::configPaths() const
-{
-	return std::vector<std::string>(1, dataPaths()[0] + "/config");
-}
-
-//FIXME: find way to at least decrease size of this ifdef (along with cleanup in CMake)
-#if defined(_WIN32)
-
 std::string VCMIDirs::userDataPath() const
 {
 	const std::string homeDir = std::getenv("userprofile");
@@ -84,6 +79,21 @@ std::string VCMIDirs::libraryName(std::string basename) const
 
 #elif defined(__APPLE__)
 
+std::string VCMIDirs::userCachePath() const
+{
+	return userDataPath();
+}
+
+std::string VCMIDirs::userConfigPath() const
+{
+	return userDataPath() + "/config";
+}
+
+std::string VCMIDirs::userSavePath() const
+{
+	return userDataPath() + "/Games";
+}
+
 std::string VCMIDirs::userDataPath() const
 {
 	// This is Cocoa code that should be normally used to get path to Application Support folder but can't use it here for now...
@@ -125,11 +135,9 @@ std::string VCMIDirs::libraryName(std::string basename) const
 
 #else
 
-std::string VCMIDirs::userDataPath() const
+std::string VCMIDirs::libraryName(std::string basename) const
 {
-	if (getenv("HOME") != nullptr )
-		return std::string(getenv("HOME")) + "/.vcmi";
-	return ".";
+	return "lib" + basename + ".so";
 }
 
 std::string VCMIDirs::libraryPath() const
@@ -147,14 +155,68 @@ std::string VCMIDirs::serverPath() const
 	return std::string(M_BIN_DIR) + "/" + "vcmiserver";
 }
 
-std::vector<std::string> VCMIDirs::dataPaths() const
+// $XDG_DATA_HOME, default: $HOME/.local/share
+std::string VCMIDirs::userDataPath() const
 {
-	return std::vector<std::string>(1, M_DATA_DIR);
+	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 ".";
 }
 
-std::string VCMIDirs::libraryName(std::string basename) const
+std::string VCMIDirs::userSavePath() const
 {
-	return "lib" + basename + ".so";
+	return userDataPath() + "/Saves";
+}
+
+// $XDG_CACHE_HOME, default: $HOME/.cache
+std::string VCMIDirs::userCachePath() const
+{
+	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 ".";
+}
+
+// $XDG_CONFIG_HOME, default: $HOME/.config
+std::string VCMIDirs::userConfigPath() const
+{
+	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 ".";
+}
+
+// $XDG_DATA_DIRS, default: /usr/local/share/:/usr/share/
+std::vector<std::string> VCMIDirs::dataPaths() const
+{
+	// construct list in reverse.
+	// in specification first directory has highest priority
+	// in vcmi fs last directory has highest priority
+
+	std::vector<std::string> ret;
+
+	if (getenv("HOME") != nullptr ) // compatibility, should be removed after 0.96
+		ret.push_back(std::string(getenv("HOME")) + "/.vcmi");
+	ret.push_back(M_DATA_DIR);
+
+	if (getenv("XDG_DATA_DIRS") != nullptr)
+	{
+		std::string dataDirsEnv = getenv("XDG_DATA_DIRS");
+		std::vector<std::string> dataDirs;
+		boost::split(dataDirs, dataDirsEnv, boost::is_any_of(":"));
+		for (auto & entry : boost::adaptors::reverse(dataDirs))
+			ret.push_back(entry + "/vcmi");
+	}
+	else
+	{
+		ret.push_back("/usr/share/");
+		ret.push_back("/usr/local/share/");
+	}
+	return ret;
 }
 
 #endif

+ 0 - 3
lib/VCMIDirs.h

@@ -33,9 +33,6 @@ public:
 	/// Path to saved games
 	std::string userSavePath() const;
 
-	/// Path to config directories, e.g. <data dir>/config. First items have higher priority
-	std::vector<std::string> configPaths() const;
-
 	/// Paths to global system-wide data directories. First items have higher priority
 	std::vector<std::string> dataPaths() const;
 

+ 5 - 4
lib/filesystem/Filesystem.cpp

@@ -140,10 +140,11 @@ void CResourceHandler::initialize()
 	initialLoader = new CFilesystemList;
 
 	for (auto & path : VCMIDirs::get().dataPaths())
-		initialLoader->addLoader(new CFilesystemLoader("", path, 0, true), false);
-
-	if (VCMIDirs::get().dataPaths().back() != VCMIDirs::get().userDataPath())
-		initialLoader->addLoader(new CFilesystemLoader("", VCMIDirs::get().userDataPath(), 0, true), false);
+	{
+		if (boost::filesystem::is_directory(path)) // some of system-provided paths may not exist
+			initialLoader->addLoader(new CFilesystemLoader("", path, 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

+ 6 - 1
vcmibuilder

@@ -163,7 +163,12 @@ fi
 
 if [[ -z "$dest_dir" ]]
 then
-	dest_dir="$HOME/.vcmi"
+	if [[ -z "$XDG_DATA_HOME" ]]
+	then
+		dest_dir="$HOME/.local/share/vcmi"
+	else
+		dest_dir="$XDG_DATA_HOME/vcmi"
+	fi
 fi
 
 temp_dir="$dest_dir"/buildertmp