summaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/files.cpp268
-rw-r--r--src/fs/files.h68
-rw-r--r--src/fs/files_unittest.cc167
-rw-r--r--src/fs/mkdir.cpp164
-rw-r--r--src/fs/mkdir.h27
-rw-r--r--src/fs/paths.cpp221
-rw-r--r--src/fs/paths.h50
-rw-r--r--src/fs/specialfolder.cpp59
-rw-r--r--src/fs/specialfolder.h33
-rw-r--r--src/fs/virtfs.cpp2
-rw-r--r--src/fs/virtfstools.cpp2
11 files changed, 1059 insertions, 2 deletions
diff --git a/src/fs/files.cpp b/src/fs/files.cpp
new file mode 100644
index 000000000..516283fe6
--- /dev/null
+++ b/src/fs/files.cpp
@@ -0,0 +1,268 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2013-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fs/files.h"
+
+#include "logger.h"
+
+#include "fs/mkdir.h"
+#if defined(ANDROID) || defined(__native_client__)
+#include "fs/paths.h"
+#include "fs/virtfs.h"
+#include "fs/virtfstools.h"
+#include "fs/virtlist.h"
+#endif // defined(ANDROID) || defined(__native_client__)
+
+#include <dirent.h>
+
+#include "debug.h"
+
+#ifdef ANDROID
+void Files::extractLocale()
+{
+ // in future need also remove all locales in local dir
+
+ const std::string fileName2 = std::string(getenv(
+ "APPDIR")).append("/locale.zip");
+ VirtFs::addZipToSearchPath(fileName2, Append_false);
+
+ const std::string localDir = std::string(getenv("APPDIR")).append("/");
+ VirtList *const rootDirs = VirtFs::enumerateFiles("locale");
+ FOR_EACH (StringVectCIter, i, rootDirs->names)
+ {
+ const std::string dir = std::string("locale/").append(*i);
+ if (VirtFs::isDirectory(dir))
+ {
+ const std::string moFile = dir + "/LC_MESSAGES/manaplus.mo";
+ if (VirtFs::exists((moFile)))
+ {
+ const std::string localFile = localDir + moFile;
+ const std::string localDir2 = localDir + dir + "/LC_MESSAGES";
+ mkdir_r(localDir2.c_str());
+ copyPhysFsFile(moFile, localFile);
+ }
+ }
+ }
+ VirtFs::freeList(rootDirs);
+ VirtFs::removeZipFromSearchPath(fileName2);
+ remove(fileName2.c_str());
+}
+#endif // ANDROID
+
+#if defined(ANDROID) || defined(__native_client__)
+
+namespace
+{
+#ifdef ANDROID
+ int mFilesCount = 0;
+#endif // ANDROID
+
+ Files::CopyFileCallbackPtr mCallbackPtr = nullptr;
+} // namespace
+
+void Files::setCopyCallBack(Files::CopyFileCallbackPtr callback)
+{
+ mCallbackPtr = callback;
+}
+
+void Files::copyPhysFsFile(const std::string &restrict inFile,
+ const std::string &restrict outFile)
+{
+ int size = 0;
+ void *const buf = VirtFs::loadFile(inFile, size);
+ FILE *const file = fopen(outFile.c_str(), "w");
+ fwrite(buf, 1, size, file);
+ fclose(file);
+ free(buf);
+#ifdef ANDROID
+ if (mCallbackPtr)
+ {
+ mCallbackPtr(mFilesCount);
+ mFilesCount ++;
+ }
+#endif // ANDROID
+}
+
+void Files::copyPhysFsDir(const std::string &restrict inDir,
+ const std::string &restrict outDir)
+{
+ mkdir_r(outDir.c_str());
+ VirtList *const files = VirtFs::enumerateFiles(inDir);
+ FOR_EACH (StringVectCIter, i, files->names)
+ {
+ const std::string file = std::string(inDir).append("/").append(*i);
+ const std::string outDir2 = std::string(outDir).append("/").append(*i);
+ if (VirtFs::isDirectory(file))
+ copyPhysFsDir(file, outDir2);
+ else
+ copyPhysFsFile(file, outDir2);
+ }
+ VirtFs::freeList(files);
+}
+
+void Files::extractZip(const std::string &restrict zipName,
+ const std::string &restrict inDir,
+ const std::string &restrict outDir)
+{
+ VirtFs::addZipToSearchPath(zipName, Append_false);
+ copyPhysFsDir(inDir, outDir);
+ VirtFs::removeZipFromSearchPath(zipName);
+ remove(zipName.c_str());
+}
+
+#endif // ANDROID __native_client__
+
+int Files::renameFile(const std::string &restrict srcName,
+ const std::string &restrict dstName)
+{
+#if defined __native_client__
+ FILE *srcFile = fopen(srcName.c_str(), "rb");
+ if (srcFile == nullptr)
+ return -1;
+ FILE *dstFile = fopen(dstName.c_str(), "w+b");
+ if (dstFile == nullptr)
+ {
+ fclose(srcFile);
+ return -1;
+ }
+
+ const int chunkSize = 500000;
+ char *buf = new char[chunkSize];
+ size_t sz = 0;
+ while ((sz = fread(buf, 1, chunkSize, srcFile)))
+ {
+ if (fwrite(buf, 1, sz, dstFile) != sz)
+ {
+ delete [] buf;
+ fclose(srcFile);
+ fclose(dstFile);
+ ::remove(dstName.c_str());
+ return -1;
+ }
+ }
+
+ delete [] buf;
+ fclose(srcFile);
+ fclose(dstFile);
+ if (!::remove(srcName.c_str()))
+ return 0;
+
+ return -1;
+#else // defined __native_client__
+
+ return ::rename(srcName.c_str(), dstName.c_str());
+#endif // defined __native_client__
+}
+
+int Files::copyFile(const std::string &restrict srcName,
+ const std::string &restrict dstName)
+{
+ FILE *srcFile = fopen(srcName.c_str(), "rb");
+ if (srcFile == nullptr)
+ return -1;
+ FILE *dstFile = fopen(dstName.c_str(), "w+b");
+ if (dstFile == nullptr)
+ {
+ fclose(srcFile);
+ return -1;
+ }
+
+ const int chunkSize = 500000;
+ char *buf = new char[chunkSize];
+ size_t sz = 0;
+ while ((sz = fread(buf, 1, chunkSize, srcFile)))
+ {
+ if (fwrite(buf, 1, sz, dstFile) != sz)
+ {
+ delete [] buf;
+ fclose(srcFile);
+ fclose(dstFile);
+ return -1;
+ }
+ }
+
+ delete [] buf;
+ fclose(srcFile);
+ fclose(dstFile);
+ return 0;
+}
+
+bool Files::existsLocal(const std::string &path)
+{
+ bool flg(false);
+ std::fstream file;
+ file.open(path.c_str(), std::ios::in);
+ if (file.is_open())
+ flg = true;
+ file.close();
+ return flg;
+}
+
+bool Files::loadTextFileLocal(const std::string &fileName,
+ StringVect &lines)
+{
+ std::ifstream file;
+ char line[501];
+
+ file.open(fileName.c_str(), std::ios::in);
+
+ if (!file.is_open())
+ {
+ logger->log("Couldn't load text file: %s", fileName.c_str());
+ return false;
+ }
+
+ while (file.getline(line, 500))
+ lines.push_back(line);
+
+ return true;
+}
+
+void Files::saveTextFile(std::string path,
+ const std::string &restrict name,
+ const std::string &restrict text)
+{
+ if (!mkdir_r(path.c_str()))
+ {
+ std::ofstream file;
+ file.open((path.append("/").append(name)).c_str(), std::ios::out);
+ if (file.is_open())
+ file << text << std::endl;
+ file.close();
+ }
+}
+
+void Files::deleteFilesInDirectory(std::string path)
+{
+ path += "/";
+ const struct dirent *next_file = nullptr;
+ DIR *const dir = opendir(path.c_str());
+
+ if (dir)
+ {
+ while ((next_file = readdir(dir)))
+ {
+ const std::string file = next_file->d_name;
+ if (file != "." && file != "..")
+ remove((path + file).c_str());
+ }
+ closedir(dir);
+ }
+}
diff --git a/src/fs/files.h b/src/fs/files.h
new file mode 100644
index 000000000..b91f7e9ab
--- /dev/null
+++ b/src/fs/files.h
@@ -0,0 +1,68 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2013-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_FILES_H
+#define UTILS_FILES_H
+
+#include "utils/stringvector.h"
+
+#include "localconsts.h"
+
+namespace Files
+{
+#ifdef ANDROID
+ void extractLocale();
+#endif // ANDROID
+
+#if defined(ANDROID) || defined(__native_client__)
+ typedef void (*CopyFileCallbackPtr) (int cnt);
+
+ void setCopyCallBack(CopyFileCallbackPtr callback);
+
+ void copyPhysFsFile(const std::string &restrict inFile,
+ const std::string &restrict outFile);
+
+ void copyPhysFsDir(const std::string &restrict inDir,
+ const std::string &restrict outDir);
+
+ void extractZip(const std::string &restrict zipName,
+ const std::string &restrict inDir,
+ const std::string &restrict outDir);
+#endif // ANDROID __native_client__
+
+ int renameFile(const std::string &restrict pFrom,
+ const std::string &restrict pTo);
+
+ int copyFile(const std::string &restrict pFrom,
+ const std::string &restrict pTo);
+
+ bool existsLocal(const std::string &path);
+
+ bool loadTextFileLocal(const std::string &fileName,
+ StringVect &lines);
+
+ void saveTextFile(std::string path,
+ const std::string &restrict name,
+ const std::string &restrict text);
+
+ void deleteFilesInDirectory(std::string path);
+} // namespace Files
+
+#endif // UTILS_FILES_H
diff --git a/src/fs/files_unittest.cc b/src/fs/files_unittest.cc
new file mode 100644
index 000000000..7b4c6ca33
--- /dev/null
+++ b/src/fs/files_unittest.cc
@@ -0,0 +1,167 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2013-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fs/files.h"
+
+#include "catch.hpp"
+#include "logger.h"
+
+#include "fs/virtfs.h"
+#include "fs/virtfstools.h"
+
+#include "utils/delete2.h"
+
+#include "resources/resourcemanager/resourcemanager.h"
+
+#include "debug.h"
+
+TEST_CASE("Files renameFile")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ const int sz = 1234567;
+ char *buf = new char[sz];
+ for (int f = 0; f < sz; f ++)
+ buf[f] = f;
+
+ const std::string name1 = "file1.test";
+ const std::string name2 = "file2.test";
+ FILE *file = fopen(name1.c_str(), "w+b");
+ fwrite(buf, 1, sz, file);
+ fclose(file);
+
+ REQUIRE(0 == Files::renameFile(name1, name2));
+ char *buf2 = new char[sz];
+ FILE *file2 = fopen(name2.c_str(), "rb");
+ REQUIRE_FALSE(nullptr == file2);
+ fread(buf2, 1, sz, file2);
+ fclose(file2);
+ ::remove(name1.c_str());
+ ::remove(name2.c_str());
+
+ for (int f = 0; f < sz; f ++)
+ REQUIRE(buf[f] == buf2[f]);
+
+ delete [] buf;
+ delete [] buf2;
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
+
+TEST_CASE("Files existsLocal")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+ REQUIRE(Files::existsLocal(VirtFs::getPath("help/about.txt")) == true);
+ REQUIRE_FALSE(Files::existsLocal(VirtFs::getPath("help/about1.txt")));
+ REQUIRE_FALSE(Files::existsLocal(VirtFs::getPath("help1/about.txt")));
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
+
+TEST_CASE("Files loadTextFileString")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+ REQUIRE(VirtFs::loadTextFileString("test/simplefile.txt") ==
+ "this is test \nfile.");
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
+
+TEST_CASE("Files loadTextFile")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ StringVect lines;
+ VirtFs::loadTextFile("test/simplefile.txt", lines);
+ REQUIRE(lines.size() == 2);
+ REQUIRE(lines[0] == "this is test ");
+ REQUIRE(lines[1] == "file.");
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
+
+TEST_CASE("Files saveTextFile")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ const std::string dir = VirtFs::getPath("test");
+ REQUIRE(dir.size() > 0);
+ Files::saveTextFile(dir, "tempfile.txt", "test line\ntext line2");
+ std::string data = VirtFs::loadTextFileString("test/tempfile.txt");
+ ::remove((dir + "/tempfile.txt").c_str());
+ REQUIRE(data == "test line\ntext line2\n");
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
+
+TEST_CASE("Files getFilesInDir")
+{
+ dirSeparator = "/";
+ logger = new Logger();
+ ResourceManager::init();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ StringVect list;
+ VirtFs::getFilesInDir("test",
+ list,
+ ".gpl");
+ REQUIRE(list.size() == 1);
+ REQUIRE(list[0] == "test/palette.gpl");
+
+ list.clear();
+ VirtFs::getFilesInDir("perserver/default",
+ list,
+ ".xml");
+ REQUIRE(list.size() == 5);
+ REQUIRE(list[0] == "perserver/default/charcreation.xml");
+ REQUIRE(list[1] == "perserver/default/deadmessages.xml");
+ REQUIRE(list[2] == "perserver/default/defaultcommands.xml");
+ REQUIRE(list[3] == "perserver/default/features.xml");
+ REQUIRE(list[4] == "perserver/default/weapons.xml");
+ ResourceManager::deleteInstance();
+ delete2(logger);
+// VirtFs::deinit();
+}
diff --git a/src/fs/mkdir.cpp b/src/fs/mkdir.cpp
new file mode 100644
index 000000000..e84ab5f28
--- /dev/null
+++ b/src/fs/mkdir.cpp
@@ -0,0 +1,164 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fs/mkdir.h"
+
+#if defined WIN32
+#include <limits.h>
+#include <windows.h>
+#endif // defined WIN32
+
+#include <sys/stat.h>
+
+#include "debug.h"
+
+#if defined WIN32
+int mkdir_r(const char *const pathname)
+{
+ if (!pathname)
+ return -1;
+
+ char tmp[PATH_MAX];
+ char tmp2[PATH_MAX];
+ char *p;
+
+ if (strlen(pathname) >= PATH_MAX - 2)
+ return -1;
+
+ strncpy(tmp, pathname, sizeof(tmp) - 1);
+ tmp[PATH_MAX - 1] = '\0';
+
+ const int len = CAST_S32(strlen(tmp));
+
+ if (len < 1 || len >= INT_MAX)
+ return -1;
+
+ // terminate the pathname with '/'
+ if (tmp[len - 1] != '/')
+ {
+ tmp[len] = '/';
+ tmp[len + 1] = '\0';
+ }
+
+ for (p = tmp; *p; p++)
+ {
+ if (*p == '/' || *p == '\\')
+ {
+ *p = '\0';
+ // ignore a slash at the beginning of a path
+ if (tmp[0] == 0)
+ {
+ *p = '/';
+ continue;
+ }
+
+ strcpy(tmp2, tmp);
+ char *p2 = tmp2 + strlen(tmp2) - 1;
+ if (*p2 == '/' || *p2 == '\\')
+ *p2 = 0;
+ // check if the name already exists, but not as directory
+ struct stat statbuf;
+ if (!stat(tmp2, &statbuf))
+ {
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ *p = '/';
+ continue;
+ }
+ else
+ return -1;
+ }
+
+ if (!CreateDirectory(tmp2, nullptr))
+ {
+ // hack, hack. just assume that x: might be a drive
+ // letter, and try again
+ if (!(strlen(tmp2) == 2 && !strcmp(tmp2 + 1, ":")))
+ return -1;
+ }
+
+ *p = '/';
+ }
+ }
+ return 0;
+}
+#else // WIN32
+
+/// Create a directory, making leading components first if necessary
+int mkdir_r(const char *const pathname)
+{
+ if (!pathname)
+ return -1;
+
+ const size_t len = CAST_SIZE(strlen(pathname));
+ char *tmp = new char[len + 2];
+ char *p = nullptr;
+
+ strcpy(tmp, pathname);
+
+ // terminate the pathname with '/'
+ if (tmp[len - 1] != '/')
+ {
+ tmp[len] = '/';
+ tmp[len + 1] = '\0';
+ }
+
+ for (p = tmp; *p; p++)
+ {
+ if (*p == '/')
+ {
+ *p = '\0';
+ // ignore a slash at the beginning of a path
+ if (tmp[0] == 0)
+ {
+ *p = '/';
+ continue;
+ }
+
+ // check if the name already exists, but not as directory
+ struct stat statbuf;
+ if (!stat(tmp, &statbuf))
+ {
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ *p = '/';
+ continue;
+ }
+ else
+ {
+ delete []tmp;
+ return -1;
+ }
+ }
+
+ if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
+ {
+ delete []tmp;
+ return -1;
+ }
+
+ *p = '/';
+ }
+ }
+ delete []tmp;
+ return 0;
+}
+#endif // WIN32
diff --git a/src/fs/mkdir.h b/src/fs/mkdir.h
new file mode 100644
index 000000000..a662daca3
--- /dev/null
+++ b/src/fs/mkdir.h
@@ -0,0 +1,27 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_MKDIR_H
+#define UTILS_MKDIR_H
+
+int mkdir_r(const char *const pathname);
+
+#endif // UTILS_MKDIR_H
diff --git a/src/fs/paths.cpp b/src/fs/paths.cpp
new file mode 100644
index 000000000..affc12285
--- /dev/null
+++ b/src/fs/paths.cpp
@@ -0,0 +1,221 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef _MSC_VER
+# include "msvc/config.h"
+#elif defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif // _MSC_VER
+
+#include "fs/paths.h"
+#include "fs/virtfs.h"
+
+#include "utils/stringutils.h"
+
+#ifdef USE_X11
+#include "fs/files.h"
+#endif // USE_X11
+
+#ifdef __native_client__
+#include <limits.h>
+#define realpath(N, R) strcpy(R, N)
+#endif // __native_client__
+
+#ifdef WIN32
+#include "fs/specialfolder.h"
+#define realpath(N, R) _fullpath((R), (N), _MAX_PATH)
+#elif defined __OpenBSD__
+#include <limits>
+#elif defined __native_client__
+#include <limits.h>
+#endif // WIN32
+
+#ifdef ANDROID
+#ifdef USE_SDL2
+#include <SDL_system.h>
+#endif // USE_SDL2
+#endif // ANDROID
+
+#include "debug.h"
+
+namespace
+{
+ std::string mPackageDir;
+} // namespace
+
+std::string getRealPath(const std::string &str)
+{
+#if defined(__OpenBSD__) || defined(__ANDROID__) || defined(__native_client__)
+ char *realPath = reinterpret_cast<char*>(calloc(PATH_MAX, sizeof(char)));
+ if (!realPath)
+ return "";
+ realpath(str.c_str(), realPath);
+#else // defined(__OpenBSD__) || defined(__ANDROID__) ||
+ // defined(__native_client__)
+
+ char *realPath = realpath(str.c_str(), nullptr);
+#endif // defined(__OpenBSD__) || defined(__ANDROID__) ||
+ // defined(__native_client__)
+
+ if (!realPath)
+ return "";
+ std::string path = realPath;
+ free(realPath);
+ return path;
+}
+
+bool isRealPath(const std::string &str)
+{
+ return str == getRealPath(str);
+}
+
+bool checkPath(const std::string &path)
+{
+ if (path.empty())
+ return true;
+ return path.find("../") == std::string::npos
+ && path.find("..\\") == std::string::npos
+ && path.find("/..") == std::string::npos
+ && path.find("\\..") == std::string::npos;
+}
+
+std::string &fixDirSeparators(std::string &str)
+{
+ if (dirSeparator[0] == '/')
+ return str;
+
+ return replaceAll(str, "/", "\\");
+}
+
+std::string removeLast(const std::string &str)
+{
+ size_t pos2 = str.rfind('/');
+ const size_t pos3 = str.rfind('\\');
+ if (pos3 != std::string::npos)
+ {
+ if (pos2 == std::string::npos || pos3 > pos2)
+ pos2 = pos3;
+ }
+ if (pos2 != std::string::npos)
+ return str.substr(0, pos2);
+ else
+ return str;
+}
+
+#ifdef WIN32
+std::string getSelfName()
+{
+ return "manaplus.exe";
+}
+
+#elif defined(__APPLE__)
+std::string getSelfName()
+{
+ return "manaplus.exe";
+}
+
+#elif defined __linux__ || defined __linux
+#include <unistd.h>
+
+std::string getSelfName()
+{
+ char buf[257];
+ const ssize_t sz = readlink("/proc/self/exe", buf, 256);
+ if (sz > 0 && sz < 256)
+ {
+ buf[sz] = 0;
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+#else // WIN32
+
+std::string getSelfName()
+{
+ return "";
+}
+
+#endif // WIN32
+
+std::string getPicturesDir()
+{
+#ifdef WIN32
+ std::string dir = getSpecialFolderLocation(CSIDL_MYPICTURES);
+ if (dir.empty())
+ dir = getSpecialFolderLocation(CSIDL_DESKTOP);
+ return dir;
+#elif defined USE_X11
+ char *xdg = getenv("XDG_CONFIG_HOME");
+ std::string file;
+ if (!xdg)
+ {
+ file = std::string(VirtFs::getUserDir()).append(
+ "/.config/user-dirs.dirs");
+ }
+ else
+ {
+ file = std::string(xdg).append("/user-dirs.dirs");
+ }
+
+ StringVect arr;
+ Files::loadTextFileLocal(file, arr);
+ FOR_EACH (StringVectCIter, it, arr)
+ {
+ std::string str = *it;
+ if (findCutFirst(str, "XDG_PICTURES_DIR=\""))
+ {
+ str = str.substr(0, str.size() - 1);
+ // use hack to replace $HOME var.
+ // if in string other vars, fallback to default path
+ replaceAll(str, "$HOME/", VirtFs::getUserDir());
+ str = getRealPath(str);
+ if (str.empty())
+ str = std::string(VirtFs::getUserDir()).append("Desktop");
+ return str;
+ }
+ }
+
+ return std::string(VirtFs::getUserDir()).append("Desktop");
+#else // WIN32
+
+ return std::string(VirtFs::getUserDir()).append("Desktop");
+#endif // WIN32
+}
+
+#ifdef ANDROID
+std::string getSdStoragePath()
+{
+ return getenv("DATADIR2");
+}
+#endif // ANDROID
+
+std::string getPackageDir()
+{
+ return mPackageDir;
+}
+
+void setPackageDir(const std::string &dir)
+{
+ mPackageDir = dir;
+}
diff --git a/src/fs/paths.h b/src/fs/paths.h
new file mode 100644
index 000000000..fc90730f3
--- /dev/null
+++ b/src/fs/paths.h
@@ -0,0 +1,50 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_PATHS_H
+#define UTILS_PATHS_H
+
+#include <string>
+
+#include "localconsts.h"
+
+std::string getRealPath(const std::string &str) A_WARN_UNUSED;
+
+bool isRealPath(const std::string &str) A_WARN_UNUSED;
+
+bool checkPath(const std::string &path) A_WARN_UNUSED;
+
+std::string &fixDirSeparators(std::string &str);
+
+std::string removeLast(const std::string &str) A_WARN_UNUSED;
+
+std::string getSelfName() A_WARN_UNUSED;
+
+std::string getPicturesDir() A_WARN_UNUSED;
+
+#ifdef ANDROID
+std::string getSdStoragePath() A_WARN_UNUSED;
+#endif // ANDROID
+
+std::string getPackageDir() A_WARN_UNUSED;
+
+void setPackageDir(const std::string &dir);
+
+#endif // UTILS_PATHS_H
diff --git a/src/fs/specialfolder.cpp b/src/fs/specialfolder.cpp
new file mode 100644
index 000000000..ee27dab4b
--- /dev/null
+++ b/src/fs/specialfolder.cpp
@@ -0,0 +1,59 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef WIN32
+#include "fs/specialfolder.h"
+#include <windows.h>
+
+#include "debug.h"
+
+/*
+ * Retrieve the pathname of special folders on win32, or an empty string
+ * on error / if the folder does not exist.
+ * See http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx for
+ * a list of folder ids
+ */
+std::string getSpecialFolderLocation(const int folderId)
+{
+ std::string ret;
+ LPITEMIDLIST pItemIdList;
+ LPMALLOC pMalloc;
+ char szPath[_MAX_PATH];
+
+ // get the item ID list for folderId
+ HRESULT hr = SHGetSpecialFolderLocation(nullptr, folderId, &pItemIdList);
+ if (hr != S_OK)
+ return ret;
+
+ // convert the ID list into a file system path
+ if (SHGetPathFromIDList(pItemIdList, szPath) == FALSE)
+ return ret;
+
+ // get the IMalloc pointer and free all resources we used
+ hr = SHGetMalloc(&pMalloc);
+ pMalloc->Free(pItemIdList);
+ pMalloc->Release();
+
+ ret = szPath;
+ return ret;
+}
+
+#endif // WIN32
diff --git a/src/fs/specialfolder.h b/src/fs/specialfolder.h
new file mode 100644
index 000000000..f2cc93c9b
--- /dev/null
+++ b/src/fs/specialfolder.h
@@ -0,0 +1,33 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2010 The Mana Developers
+ * Copyright (C) 2011-2017 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_SPECIALFOLDER_H
+#define UTILS_SPECIALFOLDER_H
+
+#ifdef WIN32
+#include <shlobj.h>
+#include <string>
+
+#include "localconsts.h"
+std::string getSpecialFolderLocation(const int folderId) A_WARN_UNUSED;
+#endif // WIN32
+
+#endif // UTILS_SPECIALFOLDER_H
diff --git a/src/fs/virtfs.cpp b/src/fs/virtfs.cpp
index 0db25f435..9b19890eb 100644
--- a/src/fs/virtfs.cpp
+++ b/src/fs/virtfs.cpp
@@ -30,7 +30,7 @@
#include <unistd.h>
#ifdef ANDROID
-#include "utils/paths.h"
+#include "fs/paths.h"
#endif // ANDROID
#include "debug.h"
diff --git a/src/fs/virtfstools.cpp b/src/fs/virtfstools.cpp
index 4d6c96c33..889739658 100644
--- a/src/fs/virtfstools.cpp
+++ b/src/fs/virtfstools.cpp
@@ -22,10 +22,10 @@
#include "logger.h"
+#include "fs/paths.h"
#include "fs/virtfs.h"
#include "fs/virtlist.h"
-#include "utils/paths.h"
#include "utils/stringutils.h"
#include <algorithm>