summaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2017-02-21 17:08:42 +0300
committerAndrei Karas <akaras@inbox.ru>2017-02-21 17:16:24 +0300
commitfc60e6391b53c7e272bbbfe81e34f64ceb92fe06 (patch)
treec2010fe985e24cd4e22346a29856a6fd003775bc /src/fs
parent01ac8bad77156f50ba57ddff5306714dc3815ef3 (diff)
downloadplus-fc60e6391b53c7e272bbbfe81e34f64ceb92fe06.tar.gz
plus-fc60e6391b53c7e272bbbfe81e34f64ceb92fe06.tar.bz2
plus-fc60e6391b53c7e272bbbfe81e34f64ceb92fe06.tar.xz
plus-fc60e6391b53c7e272bbbfe81e34f64ceb92fe06.zip
Move virt fs related files into fs directory.
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/virtfile.cpp37
-rw-r--r--src/fs/virtfile.h39
-rw-r--r--src/fs/virtfileprivate.cpp42
-rw-r--r--src/fs/virtfileprivate.h44
-rw-r--r--src/fs/virtfs.cpp304
-rw-r--r--src/fs/virtfs.h79
-rw-r--r--src/fs/virtfs_unittest.cc406
-rw-r--r--src/fs/virtfsrwops.cpp434
-rw-r--r--src/fs/virtfsrwops.h75
-rw-r--r--src/fs/virtfstools.cpp223
-rw-r--r--src/fs/virtfstools.h54
-rw-r--r--src/fs/virtlist.cpp32
-rw-r--r--src/fs/virtlist.h39
13 files changed, 1808 insertions, 0 deletions
diff --git a/src/fs/virtfile.cpp b/src/fs/virtfile.cpp
new file mode 100644
index 000000000..9f9ab6e98
--- /dev/null
+++ b/src/fs/virtfile.cpp
@@ -0,0 +1,37 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/virtfile.h"
+
+#include "fs/virtfileprivate.h"
+
+#include "utils/delete2.h"
+
+#include "debug.h"
+
+VirtFile::VirtFile() :
+ mPrivate(nullptr)
+{
+}
+
+VirtFile::~VirtFile()
+{
+ delete2(mPrivate);
+}
diff --git a/src/fs/virtfile.h b/src/fs/virtfile.h
new file mode 100644
index 000000000..cf7ded1a4
--- /dev/null
+++ b/src/fs/virtfile.h
@@ -0,0 +1,39 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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_VIRTFILE_H
+#define UTILS_VIRTFILE_H
+
+#include "localconsts.h"
+
+struct VirtFilePrivate;
+
+struct VirtFile final
+{
+ VirtFile();
+
+ A_DELETE_COPY(VirtFile)
+
+ ~VirtFile();
+
+ VirtFilePrivate *mPrivate;
+};
+
+#endif // UTILS_VIRTFILE_H
diff --git a/src/fs/virtfileprivate.cpp b/src/fs/virtfileprivate.cpp
new file mode 100644
index 000000000..6bd5d3b9a
--- /dev/null
+++ b/src/fs/virtfileprivate.cpp
@@ -0,0 +1,42 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/virtfileprivate.h"
+
+#include "debug.h"
+
+VirtFilePrivate::VirtFilePrivate() :
+ mFile(nullptr)
+{
+}
+
+VirtFilePrivate::VirtFilePrivate(PHYSFS_file *const file) :
+ mFile(file)
+{
+}
+
+VirtFilePrivate::~VirtFilePrivate()
+{
+ if (mFile != nullptr)
+ {
+ PHYSFS_close(mFile);
+ mFile = nullptr;
+ }
+}
diff --git a/src/fs/virtfileprivate.h b/src/fs/virtfileprivate.h
new file mode 100644
index 000000000..5b280de78
--- /dev/null
+++ b/src/fs/virtfileprivate.h
@@ -0,0 +1,44 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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_VIRTFILEPRIVATE_H
+#define UTILS_VIRTFILEPRIVATE_H
+
+#include "localconsts.h"
+
+PRAGMA45(GCC diagnostic push)
+PRAGMA45(GCC diagnostic ignored "-Wlong-long")
+#include <physfs.h>
+PRAGMA45(GCC diagnostic pop)
+
+struct VirtFilePrivate final
+{
+ VirtFilePrivate();
+
+ explicit VirtFilePrivate(PHYSFS_file *const file);
+
+ A_DELETE_COPY(VirtFilePrivate)
+
+ ~VirtFilePrivate();
+
+ PHYSFS_file *mFile;
+};
+
+#endif // UTILS_VIRTFILEPRIVATE_H
diff --git a/src/fs/virtfs.cpp b/src/fs/virtfs.cpp
new file mode 100644
index 000000000..0db25f435
--- /dev/null
+++ b/src/fs/virtfs.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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/virtfs.h"
+
+#include "fs/virtfile.h"
+#include "fs/virtfileprivate.h"
+#include "fs/virtlist.h"
+
+#include "utils/checkutils.h"
+
+#include <iostream>
+#include <unistd.h>
+
+#ifdef ANDROID
+#include "utils/paths.h"
+#endif // ANDROID
+
+#include "debug.h"
+
+const char *dirSeparator = nullptr;
+
+namespace VirtFs
+{
+#if defined(__native_client__)
+ void init(const std::string &restrict name A_UNUSED)
+ {
+ if (!PHYSFS_init("/fakebinary"))
+#elif defined(ANDROID)
+ void init(const std::string &restrict name A_UNUSED)
+ {
+ if (!PHYSFS_init((getRealPath(".").append("/fakebinary")).c_str()))
+#else // defined(__native_client__)
+
+ void init(const std::string &restrict name)
+ {
+ if (!PHYSFS_init(name.c_str()))
+#endif // defined(__native_client__)
+ {
+ std::cout << "Error while initializing PhysFS: "
+ << VirtFs::getLastError() << std::endl;
+ _exit(1);
+ }
+ updateDirSeparator();
+ atexit(reinterpret_cast<void(*)()>(PHYSFS_deinit));
+ }
+
+ void updateDirSeparator()
+ {
+ dirSeparator = PHYSFS_getDirSeparator();
+ }
+
+ const char *getDirSeparator()
+ {
+ return dirSeparator;
+ }
+
+ const char *getBaseDir()
+ {
+ return PHYSFS_getBaseDir();
+ }
+
+ const char *getUserDir()
+ {
+ return PHYSFS_getUserDir();
+ }
+
+ bool exists(const std::string &restrict name)
+ {
+ return PHYSFS_exists(name.c_str());
+ }
+
+ VirtList *enumerateFiles(const std::string &restrict dir)
+ {
+ char ** handle = PHYSFS_enumerateFiles(dir.c_str());
+ VirtList *const files = new VirtList;
+ if (handle == nullptr)
+ return files;
+ for (const char *const *i = handle; *i; i++)
+ {
+ std::string str = *i;
+ files->names.push_back(str);
+ }
+ PHYSFS_freeList(handle);
+ return files;
+ }
+
+ bool isDirectory(const std::string &restrict name)
+ {
+ return PHYSFS_isDirectory(name.c_str());
+ }
+
+ bool isSymbolicLink(const std::string &restrict name)
+ {
+ return PHYSFS_isSymbolicLink(name.c_str());
+ }
+
+ void freeList(VirtList *restrict const handle)
+ {
+ delete handle;
+ }
+
+ VirtFile *openRead(const std::string &restrict filename)
+ {
+ PHYSFS_file *restrict const handle = PHYSFS_openRead(
+ filename.c_str());
+ if (!handle)
+ return nullptr;
+ VirtFile *restrict const file = new VirtFile;
+ file->mPrivate = new VirtFilePrivate(handle);
+ return file;
+ }
+
+ VirtFile *openWrite(const std::string &restrict filename)
+ {
+ PHYSFS_file *restrict const handle = PHYSFS_openWrite(
+ filename.c_str());
+ if (!handle)
+ return nullptr;
+ VirtFile *restrict const file = new VirtFile;
+ file->mPrivate = new VirtFilePrivate(handle);
+ return file;
+ }
+
+ VirtFile *openAppend(const std::string &restrict filename)
+ {
+ PHYSFS_file *restrict const handle = PHYSFS_openAppend(
+ filename.c_str());
+ if (!handle)
+ return nullptr;
+ VirtFile *restrict const file = new VirtFile;
+ file->mPrivate = new VirtFilePrivate(handle);
+ return file;
+ }
+
+ bool setWriteDir(const std::string &restrict newDir)
+ {
+ return PHYSFS_setWriteDir(newDir.c_str());
+ }
+
+ bool addDirToSearchPath(const std::string &restrict newDir,
+ const Append append)
+ {
+ logger->log("Add virtual directory: " + newDir);
+ if (newDir.find(".zip") != std::string::npos)
+ {
+ reportAlways("Called addDirToSearchPath with zip archive");
+ return false;
+ }
+ return PHYSFS_addToSearchPath(newDir.c_str(),
+ append == Append_true ? 1 : 0);
+ }
+
+ bool removeDirFromSearchPath(const std::string &restrict oldDir)
+ {
+ logger->log("Remove virtual directory: " + oldDir);
+ if (oldDir.find(".zip") != std::string::npos)
+ {
+ reportAlways("Called removeDirFromSearchPath with zip archive");
+ return false;
+ }
+ return PHYSFS_removeFromSearchPath(oldDir.c_str());
+ }
+
+ bool addZipToSearchPath(const std::string &restrict newDir,
+ const Append append)
+ {
+ logger->log("Add virtual zip: " + newDir);
+ if (newDir.find(".zip") == std::string::npos)
+ {
+ reportAlways("Called addZipToSearchPath without zip archive");
+ return false;
+ }
+ return PHYSFS_addToSearchPath(newDir.c_str(),
+ append == Append_true ? 1 : 0);
+ }
+
+ bool removeZipFromSearchPath(const std::string &restrict oldDir)
+ {
+ logger->log("Remove virtual zip: " + oldDir);
+ if (oldDir.find(".zip") == std::string::npos)
+ {
+ reportAlways("Called removeZipFromSearchPath without zip archive");
+ return false;
+ }
+ return PHYSFS_removeFromSearchPath(oldDir.c_str());
+ }
+
+ std::string getRealDir(const std::string &restrict filename)
+ {
+ const char *const str = PHYSFS_getRealDir(filename.c_str());
+ if (str == nullptr)
+ return std::string();
+ return str;
+ }
+
+ bool mkdir(const std::string &restrict dirname)
+ {
+ return PHYSFS_mkdir(dirname.c_str());
+ }
+
+ bool remove(const std::string &restrict filename)
+ {
+ return PHYSFS_delete(filename.c_str());
+ }
+
+ bool deinit()
+ {
+ if (PHYSFS_deinit() != 0)
+ {
+ logger->log("Physfs deinit error: %s",
+ VirtFs::getLastError());
+ return false;
+ }
+ return true;
+ }
+
+ void permitLinks(const bool val)
+ {
+ PHYSFS_permitSymbolicLinks(val ? 1 : 0);
+ }
+
+ const char *getLastError()
+ {
+ return PHYSFS_getLastError();
+ }
+
+ int close(VirtFile *restrict const file)
+ {
+ if (file == nullptr)
+ return 0;
+ delete file;
+ return 1;
+ }
+
+ int64_t read(VirtFile *restrict const file,
+ void *restrict const buffer,
+ const uint32_t objSize,
+ const uint32_t objCount)
+ {
+ if (file == nullptr)
+ return 0;
+ return PHYSFS_read(file->mPrivate->mFile,
+ buffer,
+ objSize,
+ objCount);
+ }
+
+ int64_t write(VirtFile *restrict const file,
+ const void *restrict const buffer,
+ const uint32_t objSize,
+ const uint32_t objCount)
+ {
+ if (file == nullptr)
+ return 0;
+ return PHYSFS_write(file->mPrivate->mFile,
+ buffer,
+ objSize,
+ objCount);
+ }
+
+ int64_t fileLength(VirtFile *restrict const file)
+ {
+ if (file == nullptr)
+ return -1;
+ return PHYSFS_fileLength(file->mPrivate->mFile);
+ }
+
+ int64_t tell(VirtFile *restrict const file)
+ {
+ if (file == nullptr)
+ return -1;
+ return PHYSFS_tell(file->mPrivate->mFile);
+ }
+
+ int seek(VirtFile *restrict const file,
+ const uint64_t pos)
+ {
+ return PHYSFS_seek(file->mPrivate->mFile,
+ pos);
+ }
+
+ int eof(VirtFile *restrict const file)
+ {
+ return PHYSFS_eof(file->mPrivate->mFile);
+ }
+} // namespace VirtFs
diff --git a/src/fs/virtfs.h b/src/fs/virtfs.h
new file mode 100644
index 000000000..e0f188cfb
--- /dev/null
+++ b/src/fs/virtfs.h
@@ -0,0 +1,79 @@
+/*
+ * 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_VIRTFS_H
+#define UTILS_VIRTFS_H
+
+#include "enums/simpletypes/append.h"
+
+#include "localconsts.h"
+
+#include <string>
+
+struct VirtFile;
+struct VirtList;
+
+namespace VirtFs
+{
+ void init(const std::string &restrict name);
+ void updateDirSeparator();
+ const char *getDirSeparator();
+ const char *getBaseDir();
+ const char *getUserDir();
+ bool exists(const std::string &restrict name);
+ VirtList *enumerateFiles(const std::string &restrict dir) RETURNS_NONNULL;
+ bool isDirectory(const std::string &restrict name);
+ bool isSymbolicLink(const std::string &restrict name);
+ void freeList(VirtList *restrict const handle);
+ VirtFile *openRead(const std::string &restrict filename);
+ VirtFile *openWrite(const std::string &restrict filename);
+ VirtFile *openAppend(const std::string &restrict filename);
+ bool setWriteDir(const std::string &restrict newDir);
+ bool addDirToSearchPath(const std::string &restrict newDir,
+ const Append append);
+ bool removeDirFromSearchPath(const std::string &restrict oldDir);
+ bool addZipToSearchPath(const std::string &restrict newDir,
+ const Append append);
+ bool removeZipFromSearchPath(const std::string &restrict oldDir);
+ std::string getRealDir(const std::string &restrict filename);
+ bool mkdir(const std::string &restrict dirName);
+ bool remove(const std::string &restrict filename);
+ bool deinit();
+ void permitLinks(const bool val);
+ const char *getLastError();
+ int64_t read(VirtFile *restrict const handle,
+ void *restrict const buffer,
+ const uint32_t objSize,
+ const uint32_t objCount);
+ int64_t write(VirtFile *restrict const file,
+ const void *restrict const buffer,
+ const uint32_t objSize,
+ const uint32_t objCount);
+ int close(VirtFile *restrict const file);
+ int64_t fileLength(VirtFile *restrict const file);
+ int64_t tell(VirtFile *restrict const file);
+ int seek(VirtFile *restrict const file,
+ const uint64_t pos);
+ int eof(VirtFile *restrict const file);
+} // namespace VirtFs
+
+extern const char *dirSeparator;
+
+#endif // UTILS_VIRTFS_H
diff --git a/src/fs/virtfs_unittest.cc b/src/fs/virtfs_unittest.cc
new file mode 100644
index 000000000..4f3a74a7b
--- /dev/null
+++ b/src/fs/virtfs_unittest.cc
@@ -0,0 +1,406 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2016-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 "catch.hpp"
+
+#include "fs/virtfs.h"
+#include "fs/virtfstools.h"
+#include "fs/virtlist.h"
+
+#include "utils/checkutils.h"
+#include "utils/delete2.h"
+
+#include "debug.h"
+
+TEST_CASE("VirtFs dirSeparator")
+{
+ REQUIRE(dirSeparator != nullptr);
+ REQUIRE(VirtFs::getDirSeparator() == std::string(dirSeparator));
+ VirtFs::updateDirSeparator();
+ REQUIRE(dirSeparator != nullptr);
+ REQUIRE(VirtFs::getDirSeparator() == std::string(dirSeparator));
+}
+
+TEST_CASE("VirtFs getBaseDir")
+{
+ REQUIRE(VirtFs::getBaseDir() != nullptr);
+}
+
+TEST_CASE("VirtFs getUserDir")
+{
+ REQUIRE(VirtFs::getUserDir() != nullptr);
+}
+
+TEST_CASE("VirtFs exists")
+{
+ logger = new Logger();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ REQUIRE(VirtFs::exists("test/units.xml") == true);
+ REQUIRE(VirtFs::exists("test/units123.xml") == false);
+ REQUIRE(VirtFs::exists("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::exists("units.xml") == false);
+
+ VirtFs::addDirToSearchPath("data/test", Append_false);
+ VirtFs::addDirToSearchPath("../data/test", Append_false);
+
+ REQUIRE(VirtFs::exists("test/units.xml") == true);
+ REQUIRE(VirtFs::exists("test/units123.xml") == false);
+ REQUIRE(VirtFs::exists("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::exists("units.xml") == true);
+
+ VirtFs::removeDirFromSearchPath("data/test");
+ VirtFs::removeDirFromSearchPath("../data/test");
+
+ REQUIRE(VirtFs::exists("test/units.xml") == true);
+ REQUIRE(VirtFs::exists("test/units123.xml") == false);
+ REQUIRE(VirtFs::exists("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::exists("units.xml") == false);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+static void removeTemp(StringVect &restrict list)
+{
+ int cnt = 0;
+ std::sort(list.begin(), list.end());
+
+ FOR_EACH (StringVectIter, it, list)
+ {
+ if (*it != "serverlistplus.xml.part")
+ {
+ logger->log("file: %d %s",
+ cnt,
+ (*it).c_str());
+ cnt ++;
+ }
+ }
+
+ FOR_EACH (StringVectIter, it, list)
+ {
+ if (*it == "serverlistplus.xml.part")
+ {
+ list.erase(it);
+ return;
+ }
+ }
+}
+
+TEST_CASE("VirtFs enumerateFiles")
+{
+ logger = new Logger;
+
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ VirtList *list = nullptr;
+
+ const int cnt1 = VirtFs::exists("test/test2.txt") ? 23 : 22;
+ const int cnt2 = 23;
+
+ VirtFs::permitLinks(false);
+ list = VirtFs::enumerateFiles("test");
+ removeTemp(list->names);
+ const size_t sz = list->names.size();
+ REQUIRE(sz == cnt1);
+ VirtFs::freeList(list);
+
+ VirtFs::permitLinks(true);
+ list = VirtFs::enumerateFiles("test");
+ removeTemp(list->names);
+ REQUIRE(list->names.size() == cnt2);
+ VirtFs::freeList(list);
+
+ VirtFs::permitLinks(false);
+ list = VirtFs::enumerateFiles("test");
+ removeTemp(list->names);
+ REQUIRE(list->names.size() == cnt1);
+ VirtFs::freeList(list);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+TEST_CASE("VirtFs isDirectory")
+{
+ logger = new Logger();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ REQUIRE(VirtFs::isDirectory("test/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test/units.xml/") == false);
+ REQUIRE(VirtFs::isDirectory("test//units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test/units123.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test//units123.xml") == false);
+ REQUIRE(VirtFs::isDirectory("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("tesQ//units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test") == true);
+ REQUIRE(VirtFs::isDirectory("test/") == true);
+ REQUIRE(VirtFs::isDirectory("test//") == true);
+ REQUIRE(VirtFs::isDirectory("test/dir1") == true);
+ REQUIRE(VirtFs::isDirectory("test//dir1") == true);
+ REQUIRE(VirtFs::isDirectory("test//dir1/") == true);
+ REQUIRE(VirtFs::isDirectory("test//dir1//") == true);
+ REQUIRE(VirtFs::isDirectory("test/dir1/") == true);
+ REQUIRE(VirtFs::isDirectory("test/dir1//") == true);
+ REQUIRE(VirtFs::isDirectory("testQ") == false);
+ REQUIRE(VirtFs::isDirectory("testQ/") == false);
+ REQUIRE(VirtFs::isDirectory("testQ//") == false);
+
+ VirtFs::addDirToSearchPath("data/test", Append_false);
+ VirtFs::addDirToSearchPath("../data/test", Append_false);
+
+ REQUIRE(VirtFs::isDirectory("test/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test/units.xml/") == false);
+ REQUIRE(VirtFs::isDirectory("test//units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test/units123.xml") == false);
+ REQUIRE(VirtFs::isDirectory("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test") == true);
+ REQUIRE(VirtFs::isDirectory("testQ") == false);
+ REQUIRE(VirtFs::isDirectory("test/dir1") == true);
+
+ VirtFs::removeDirFromSearchPath("data/test");
+ VirtFs::removeDirFromSearchPath("../data/test");
+
+ REQUIRE(VirtFs::isDirectory("test/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("test/units123.xml") == false);
+ REQUIRE(VirtFs::isDirectory("tesQ/units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("units.xml") == false);
+ REQUIRE(VirtFs::isDirectory("units.xml/") == false);
+ REQUIRE(VirtFs::isDirectory("test") == true);
+ REQUIRE(VirtFs::isDirectory("test/") == true);
+ REQUIRE(VirtFs::isDirectory("testQ") == false);
+ REQUIRE(VirtFs::isDirectory("test/dir1") == true);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+TEST_CASE("VirtFs openRead")
+{
+ logger = new Logger();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ VirtFile *file = nullptr;
+
+ file = VirtFs::openRead("test/units.xml");
+ REQUIRE(file != nullptr);
+ VirtFs::close(file);
+ file = VirtFs::openRead("test/units123.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("tesQ/units.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("units.xml");
+ REQUIRE(file == nullptr);
+// file = VirtFs::openRead("test");
+// REQUIRE(file == nullptr);
+ file = VirtFs::openRead("testQ");
+ REQUIRE(file == nullptr);
+
+ VirtFs::addDirToSearchPath("data/test", Append_false);
+ VirtFs::addDirToSearchPath("../data/test", Append_false);
+
+ file = VirtFs::openRead("test/units.xml");
+ REQUIRE(file != nullptr);
+ VirtFs::close(file);
+ file = VirtFs::openRead("test/units123.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("tesQ/units.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("units.xml");
+ REQUIRE(file != nullptr);
+ VirtFs::close(file);
+// file = VirtFs::openRead("test");
+// REQUIRE(file == nullptr);
+ file = VirtFs::openRead("testQ");
+ REQUIRE(file == nullptr);
+
+ VirtFs::removeDirFromSearchPath("data/test");
+ VirtFs::removeDirFromSearchPath("../data/test");
+
+ file = VirtFs::openRead("test/units.xml");
+ REQUIRE(file != nullptr);
+ VirtFs::close(file);
+ file = VirtFs::openRead("test/units123.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("tesQ/units.xml");
+ REQUIRE(file == nullptr);
+ file = VirtFs::openRead("units.xml");
+ REQUIRE(file == nullptr);
+// file = VirtFs::openRead("test");
+// REQUIRE(file == nullptr);
+ file = VirtFs::openRead("testQ");
+ REQUIRE(file == nullptr);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+TEST_CASE("VirtFs addZipToSearchPath")
+{
+ // +++ need implement
+}
+
+TEST_CASE("VirtFs removeZipFromSearchPath")
+{
+ // +++ need implement
+}
+
+TEST_CASE("VirtFs getRealDir")
+{
+ logger = new Logger();
+ REQUIRE(VirtFs::getRealDir(".") == "");
+ REQUIRE(VirtFs::getRealDir("..") == "");
+ const bool dir1 = VirtFs::addDirToSearchPath("data", Append_false);
+ REQUIRE((dir1 || VirtFs::addDirToSearchPath("../data", Append_false)) ==
+ true);
+ if (dir1 == true)
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "data");
+ }
+ else
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "../data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "../data");
+ }
+ REQUIRE(VirtFs::getRealDir("zzz") == "");
+
+ VirtFs::addDirToSearchPath("data/test", Append_false);
+ VirtFs::addDirToSearchPath("../data/test", Append_false);
+ if (dir1 == true)
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "data");
+ REQUIRE(VirtFs::getRealDir("test.txt") ==
+ "data/test");
+ }
+ else
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "../data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "../data");
+ REQUIRE(VirtFs::getRealDir("test.txt") ==
+ "../data/test");
+ }
+ REQUIRE(VirtFs::getRealDir("zzz") == "");
+
+ VirtFs::removeDirFromSearchPath("data/test");
+ VirtFs::removeDirFromSearchPath("../data/test");
+
+ if (dir1 == true)
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "data");
+ }
+ else
+ {
+ REQUIRE(VirtFs::getRealDir("test") == "../data");
+ REQUIRE(VirtFs::getRealDir("test/test.txt") ==
+ "../data");
+ }
+ REQUIRE(VirtFs::getRealDir("zzz") == "");
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+TEST_CASE("VirtFs permitLinks")
+{
+ logger = new Logger();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ const int cnt1 = VirtFs::exists("test/test2.txt") ? 22 : 21;
+ const int cnt2 = 22;
+
+ StringVect list;
+ VirtFs::permitLinks(false);
+ VirtFs::getFiles("test", list);
+ removeTemp(list);
+ const size_t sz = list.size();
+ REQUIRE(sz == cnt1);
+
+ list.clear();
+ VirtFs::permitLinks(true);
+ VirtFs::getFiles("test", list);
+ removeTemp(list);
+ REQUIRE(list.size() == cnt2);
+
+ list.clear();
+ VirtFs::permitLinks(false);
+ VirtFs::getFiles("test", list);
+ removeTemp(list);
+ REQUIRE(list.size() == cnt1);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
+
+TEST_CASE("VirtFs read")
+{
+ logger = new Logger();
+ VirtFs::addDirToSearchPath("data", Append_false);
+ VirtFs::addDirToSearchPath("../data", Append_false);
+
+ VirtFile *file = VirtFs::openRead("test/test.txt");
+ REQUIRE(file != nullptr);
+ REQUIRE(VirtFs::fileLength(file) == 23);
+ const int fileSize = VirtFs::fileLength(file);
+
+ void *restrict buffer = calloc(fileSize + 1, 1);
+ REQUIRE(VirtFs::read(file, buffer, 1, fileSize) == fileSize);
+ REQUIRE(strcmp(static_cast<char*>(buffer),
+ "test line 1\ntest line 2") == 0);
+ REQUIRE(VirtFs::tell(file) == fileSize);
+ REQUIRE(VirtFs::eof(file) == true);
+
+ free(buffer);
+ buffer = calloc(fileSize + 1, 1);
+ REQUIRE(VirtFs::seek(file, 12) != 0);
+ REQUIRE(VirtFs::eof(file) == false);
+ REQUIRE(VirtFs::tell(file) == 12);
+ REQUIRE(VirtFs::read(file, buffer, 1, 11) == 11);
+ REQUIRE(strcmp(static_cast<char*>(buffer),
+ "test line 2") == 0);
+ REQUIRE(VirtFs::eof(file) == true);
+
+ VirtFs::close(file);
+ free(buffer);
+
+ VirtFs::removeDirFromSearchPath("data");
+ VirtFs::removeDirFromSearchPath("../data");
+ delete2(logger);
+}
diff --git a/src/fs/virtfsrwops.cpp b/src/fs/virtfsrwops.cpp
new file mode 100644
index 000000000..036d8bb60
--- /dev/null
+++ b/src/fs/virtfsrwops.cpp
@@ -0,0 +1,434 @@
+/*
+ * 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/>.
+ */
+
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * SDL falls under the LGPL license. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ *
+ * Copyright (C) 2012-2017 The ManaPlus Developers
+ */
+
+#include "fs/virtfsrwops.h"
+
+#include "logger.h"
+
+#include "fs/virtfs.h"
+
+#ifdef DEBUG_VIRTFS
+#include "utils/debugmemoryobject.h"
+
+#include <map>
+#endif // DEBUG_VIRTFS
+
+#include "utils/fuzzer.h"
+
+#include "debug.h"
+
+#ifdef USE_SDL2
+#define RWOPSINT int64_t
+#define RWOPSSIZE size_t
+#else // USE_SDL2
+#define RWOPSINT int32_t
+#define RWOPSSIZE int
+#endif // USE_SDL2
+
+#ifdef DUMP_LEAKED_RESOURCES
+static int openedRWops = 0;
+#endif // DUMP_LEAKED_RESOURCES
+
+#ifdef DEBUG_VIRTFS
+namespace
+{
+ std::map<void*, VirtFs::DebugMemoryObject*> mRWops;
+} // namespace
+
+static SDL_RWops *addDebugRWops(SDL_RWops *restrict const rwops,
+ const char *restrict const name,
+ const char *restrict const file,
+ const unsigned line)
+{
+ if (!rwops)
+ return nullptr;
+
+ mRWops[rwops] = new VirtFs::DebugMemoryObject(name, file, line);
+ return rwops;
+}
+
+static void deleteDebugRWops(SDL_RWops *const rwops)
+{
+ if (!rwops)
+ return;
+
+ std::map<void*, VirtFs::DebugMemoryObject*>::iterator it =
+ mRWops.find(rwops);
+ if (it == mRWops.end())
+ {
+ logger->log("bad RWops delete: %p", static_cast<void*>(rwops));
+ }
+ else
+ {
+ VirtFs::DebugMemoryObject *const obj = (*it).second;
+ if (obj)
+ {
+ mRWops.erase(rwops);
+ delete obj;
+ }
+ }
+}
+
+void VirtFs::reportLeaks()
+{
+ if (!mRWops.empty())
+ {
+ logger->log("RWops leaks detected");
+ std::map<void*, VirtFs::DebugMemoryObject*>::iterator it =
+ mRWops.begin();
+ const std::map<void*, VirtFs::DebugMemoryObject*>::iterator
+ it_end = mRWops.end();
+ for (; it != it_end; ++it)
+ {
+ VirtFs::DebugMemoryObject *obj = (*it).second;
+ if (obj)
+ {
+ logger->log("file: %s at %s", obj->mName.c_str(),
+ obj->mAddFile.c_str());
+ delete obj;
+ }
+ }
+ mRWops.clear();
+ }
+}
+#endif // DEBUG_VIRTFS
+
+static RWOPSINT virtfsrwops_seek(SDL_RWops *const rw,
+ const RWOPSINT offset,
+ const int whence)
+{
+ if (!rw)
+ return -1;
+ VirtFile *const handle = static_cast<VirtFile *const>(
+ rw->hidden.unknown.data1);
+ RWOPSINT pos = 0;
+
+ if (whence == SEEK_SET)
+ {
+ pos = offset;
+ } /* if */
+ else if (whence == SEEK_CUR)
+ {
+ const int64_t current = VirtFs::tell(handle);
+ if (current == -1)
+ {
+ logger->assertLog(
+ "virtfsrwops_seek: Can't find position in file: %s",
+ VirtFs::getLastError());
+ return -1;
+ } /* if */
+
+ pos = CAST_S32(current);
+ if (static_cast<int64_t>(pos) != current)
+ {
+ logger->assertLog("virtfsrwops_seek: "
+ "Can't fit current file position in an int!");
+ return -1;
+ } /* if */
+
+ if (offset == 0) /* this is a "tell" call. We're done. */
+ return pos;
+
+ pos += offset;
+ } /* else if */
+ else if (whence == SEEK_END)
+ {
+ const int64_t len = VirtFs::fileLength(handle);
+ if (len == -1)
+ {
+ logger->assertLog("virtfsrwops_seek:Can't find end of file: %s",
+ VirtFs::getLastError());
+ return -1;
+ } /* if */
+
+ pos = static_cast<RWOPSINT>(len);
+ if (static_cast<int64_t>(pos) != len)
+ {
+ logger->assertLog("virtfsrwops_seek: "
+ "Can't fit end-of-file position in an int!");
+ return -1;
+ } /* if */
+
+ pos += offset;
+ } /* else if */
+ else
+ {
+ logger->assertLog("virtfsrwops_seek: Invalid 'whence' parameter.");
+ return -1;
+ } /* else */
+
+ if (pos < 0)
+ {
+ logger->assertLog("virtfsrwops_seek: "
+ "Attempt to seek past start of file.");
+ return -1;
+ } /* if */
+
+ if (!VirtFs::seek(handle, static_cast<uint64_t>(pos)))
+ {
+ logger->assertLog("virtfsrwops_seek: seek error: %s",
+ VirtFs::getLastError());
+ return -1;
+ } /* if */
+
+ return pos;
+} /* virtfsrwops_seek */
+
+static RWOPSSIZE virtfsrwops_read(SDL_RWops *const rw,
+ void *const ptr,
+ const RWOPSSIZE size,
+ const RWOPSSIZE maxnum)
+{
+ if (!rw)
+ return 0;
+ VirtFile *const handle = static_cast<VirtFile *const>(
+ rw->hidden.unknown.data1);
+ const int64_t rc = VirtFs::read(handle, ptr,
+ CAST_U32(size),
+ CAST_U32(maxnum));
+ if (rc != static_cast<int64_t>(maxnum))
+ {
+ if (!VirtFs::eof(handle)) /* not EOF? Must be an error. */
+ {
+ logger->assertLog("virtfsrwops_seek: read error: %s",
+ VirtFs::getLastError());
+ }
+ } /* if */
+
+ return CAST_S32(rc);
+} /* virtfsrwops_read */
+
+static RWOPSSIZE virtfsrwops_write(SDL_RWops *const rw,
+ const void *const ptr,
+ const RWOPSSIZE size,
+ const RWOPSSIZE num)
+{
+ if (!rw)
+ return 0;
+ VirtFile *const handle = static_cast<VirtFile *const>(
+ rw->hidden.unknown.data1);
+ const int64_t rc = VirtFs::write(handle, ptr,
+ CAST_U32(size),
+ CAST_U32(num));
+ if (rc != static_cast<int64_t>(num))
+ {
+ logger->assertLog("virtfsrwops_seek: write error: %s",
+ VirtFs::getLastError());
+ }
+
+ return CAST_S32(rc);
+} /* virtfsrwops_write */
+
+static int virtfsrwops_close(SDL_RWops *const rw)
+{
+ if (!rw)
+ return 0;
+ VirtFile *const handle = static_cast<VirtFile*>(
+ rw->hidden.unknown.data1);
+ if (!VirtFs::close(handle))
+ {
+ logger->assertLog("virtfsrwops_seek: close error: %s",
+ VirtFs::getLastError());
+ return -1;
+ } /* if */
+
+ SDL_FreeRW(rw);
+#ifdef DUMP_LEAKED_RESOURCES
+ if (openedRWops <= 0)
+ logger->assertLog("virtfsrwops_seek: closing already closed RWops");
+ openedRWops --;
+#endif // DUMP_LEAKED_RESOURCES
+#ifdef DEBUG_VIRTFS
+ deleteDebugRWops(rw);
+#endif // DEBUG_VIRTFS
+
+ return 0;
+} /* virtfsrwops_close */
+
+#ifdef USE_SDL2
+static RWOPSINT virtfsrwops_size(SDL_RWops *const rw)
+{
+ VirtFile *const handle = static_cast<VirtFile *const>(
+ rw->hidden.unknown.data1);
+ return VirtFs::fileLength(handle);
+} /* virtfsrwops_size */
+#endif // USE_SDL2
+
+static SDL_RWops *create_rwops(VirtFile *const file)
+{
+ SDL_RWops *retval = nullptr;
+
+ if (!file)
+ {
+ logger->assertLog("virtfsrwops_seek: create rwops error: %s",
+ VirtFs::getLastError());
+ }
+ else
+ {
+ retval = SDL_AllocRW();
+ if (retval)
+ {
+#ifdef USE_SDL2
+ retval->size = &virtfsrwops_size;
+#endif // USE_SDL2
+
+ retval->seek = &virtfsrwops_seek;
+ retval->read = &virtfsrwops_read;
+ retval->write = &virtfsrwops_write;
+ retval->close = &virtfsrwops_close;
+ retval->hidden.unknown.data1 = file;
+ } /* if */
+#ifdef DUMP_LEAKED_RESOURCES
+ openedRWops ++;
+#endif // DUMP_LEAKED_RESOURCES
+ } /* else */
+
+ return retval;
+} /* create_rwops */
+
+SDL_RWops *VirtFs::MakeRWops(VirtFile *const handle)
+{
+ SDL_RWops *retval = nullptr;
+ if (!handle)
+ {
+ logger->assertLog("virtfsrwops_seek: NULL pointer passed to "
+ "RWopsmakeRWops().");
+ }
+ else
+ {
+ retval = create_rwops(handle);
+ }
+
+ return retval;
+} /* RWopsmakeRWops */
+
+#ifdef __APPLE__
+static bool checkFilePath(const std::string &restrict fname)
+{
+ if (fname.empty())
+ return false;
+ if (!VirtFs::exists(fname) || VirtFs::isDirectory(fname))
+ return false;
+ return true;
+}
+#endif // __APPLE__
+
+#ifdef DEBUG_VIRTFS
+#undef RWopsOpenRead
+SDL_RWops *VirtFs::RWopsOpenRead(const std::string &restrict fname,
+ const char *restrict const file,
+ const unsigned line)
+#else // DEBUG_VIRTFS
+SDL_RWops *VirtFs::RWopsOpenRead(const std::string &restrict fname)
+#endif // DEBUG_VIRTFS
+{
+ BLOCK_START("RWopsopenRead")
+#ifdef __APPLE__
+ if (!checkFilePath(fname))
+ return nullptr;
+#endif // __APPLE__
+#ifdef USE_FUZZER
+ if (Fuzzer::conditionTerminate(fname))
+ return nullptr;
+#endif // USE_FUZZER
+#ifdef USE_PROFILER
+
+#ifdef DEBUG_VIRTFS
+ SDL_RWops *const ret = addDebugRWops(
+ create_rwops(VirtFs::openRead(fname)),
+ fname,
+ file,
+ line);
+#else // DEBUG_VIRTFS
+ SDL_RWops *const ret = create_rwops(VirtFs::openRead(fname));
+#endif // DEBUG_VIRTFS
+
+ BLOCK_END("RWopsopenRead")
+ return ret;
+#else // USE_PROFILER
+
+#ifdef DEBUG_VIRTFS
+ return addDebugRWops(
+ create_rwops(VirtFs::openRead(fname)),
+ fname,
+ file,
+ line);
+#else // DEBUG_VIRTFS
+ return create_rwops(VirtFs::openRead(fname));
+#endif // DEBUG_VIRTFS
+#endif // USE_PROFILER
+} /* RWopsopenRead */
+
+SDL_RWops *VirtFs::RWopsOpenWrite(const std::string &restrict fname)
+{
+#ifdef __APPLE__
+ if (!checkFilePath(fname))
+ return nullptr;
+#endif // __APPLE__
+
+ return create_rwops(VirtFs::openWrite(fname));
+} /* RWopsopenWrite */
+
+SDL_RWops *VirtFs::RWopsOpenAppend(const std::string &restrict fname)
+{
+#ifdef __APPLE__
+ if (!checkFilePath(fname))
+ return nullptr;
+#endif // __APPLE__
+
+ return create_rwops(VirtFs::openAppend(fname));
+} /* RWopsopenAppend */
+
+#ifdef DUMP_LEAKED_RESOURCES
+void VirtFs::reportRWops()
+{
+ if (openedRWops)
+ {
+ logger->assertLog("virtfsrwops_seek: leaking RWops: %d",
+ openedRWops);
+ }
+}
+#endif // DUMP_LEAKED_RESOURCES
+
+/* end of physfsrwops.c ... */
diff --git a/src/fs/virtfsrwops.h b/src/fs/virtfsrwops.h
new file mode 100644
index 000000000..82bb740de
--- /dev/null
+++ b/src/fs/virtfsrwops.h
@@ -0,0 +1,75 @@
+/*
+ * 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/>.
+ */
+
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * SDL falls under the LGPL license. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ *
+ * Copyright (C) 2012-2017 The ManaPlus Developers
+ */
+
+#ifndef UTILS_PHYSFSRWOPS_H
+#define UTILS_PHYSFSRWOPS_H
+
+#include "localconsts.h"
+
+#include <string>
+#include <SDL_rwops.h>
+
+struct VirtFile;
+
+namespace VirtFs
+{
+#ifdef DEBUG_VIRTFS
+ SDL_RWops *RWopsOpenRead(const std::string &restrict fname,
+ const char *restrict const file,
+ const unsigned line);
+ void reportLeaks();
+#else // DEBUG_VIRTFS
+ SDL_RWops *RWopsOpenRead(const std::string &restrict fname);
+#endif // DEBUG_VIRTFS
+
+ SDL_RWops *RWopsOpenWrite(const std::string &restrict fname) A_WARN_UNUSED;
+ SDL_RWops *RWopsOpenAppend(const std::string &restrict fname)
+ A_WARN_UNUSED;
+ SDL_RWops *MakeRWops(VirtFile *const handle) A_WARN_UNUSED;
+#ifdef DUMP_LEAKED_RESOURCES
+ void reportRWops();
+#endif // DUMP_LEAKED_RESOURCES
+} // namespace VirtFs
+
+#endif // UTILS_PHYSFSRWOPS_H
diff --git a/src/fs/virtfstools.cpp b/src/fs/virtfstools.cpp
new file mode 100644
index 000000000..4d6c96c33
--- /dev/null
+++ b/src/fs/virtfstools.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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/virtfstools.h"
+
+#include "logger.h"
+
+#include "fs/virtfs.h"
+#include "fs/virtlist.h"
+
+#include "utils/paths.h"
+#include "utils/stringutils.h"
+
+#include <algorithm>
+#include <sstream>
+
+#include "debug.h"
+
+namespace VirtFs
+{
+ void *loadFile(const std::string &restrict fileName,
+ int &restrict fileSize)
+ {
+ // Attempt to open the specified file using PhysicsFS
+ VirtFile *restrict const file = VirtFs::openRead(fileName);
+
+ if (!file)
+ {
+ logger->log("Warning: Failed to load %s: %s",
+ fileName.c_str(),
+ VirtFs::getLastError());
+ return nullptr;
+ }
+
+ logger->log("Loaded %s/%s",
+ VirtFs::getRealDir(fileName).c_str(),
+ fileName.c_str());
+
+ fileSize = CAST_S32(VirtFs::fileLength(file));
+ // Allocate memory and load the file
+ void *restrict const buffer = calloc(fileSize, 1);
+ VirtFs::read(file, buffer, 1, fileSize);
+ VirtFs::close(file);
+
+ return buffer;
+ }
+
+ void searchAndAddArchives(const std::string &restrict path,
+ const std::string &restrict ext,
+ const Append append)
+ {
+ VirtList *const list = VirtFs::enumerateFiles(path);
+ FOR_EACH (StringVectCIter, i, list->names)
+ {
+ const std::string str = *i;
+ const size_t len = str.size();
+
+ if (len > ext.length() &&
+ !ext.compare(str.substr(len - ext.length())))
+ {
+ const std::string file = path + str;
+ const std::string realPath = VirtFs::getRealDir(file);
+ VirtFs::addZipToSearchPath(std::string(realPath).append(
+ dirSeparator).append(file), append);
+ }
+ }
+ VirtFs::freeList(list);
+ }
+
+ void searchAndRemoveArchives(const std::string &restrict path,
+ const std::string &restrict ext)
+ {
+ VirtList *const list = VirtFs::enumerateFiles(path);
+ FOR_EACH (StringVectCIter, i, list->names)
+ {
+ const std::string str = *i;
+ const size_t len = str.size();
+ if (len > ext.length() &&
+ !ext.compare(str.substr(len - ext.length())))
+ {
+ const std::string file = path + str;
+ const std::string realPath = VirtFs::getRealDir(file);
+ VirtFs::removeZipFromSearchPath(std::string(
+ realPath).append(
+ dirSeparator).append(
+ file));
+ }
+ }
+ VirtFs::freeList(list);
+ }
+
+ void getFilesWithDir(const std::string &path,
+ StringVect &list)
+ {
+ VirtList *const fonts = VirtFs::enumerateFiles(path);
+ FOR_EACH (StringVectCIter, i, fonts->names)
+ {
+ if (!VirtFs::isDirectory(path + *i))
+ list.push_back(path + *i);
+ }
+ VirtFs::freeList(fonts);
+ }
+
+ void getFilesInDir(const std::string &dir,
+ StringVect &list,
+ const std::string &ext)
+ {
+ const std::string path = dir + "/";
+ StringVect tempList;
+ VirtFs::getFilesWithDir(path, tempList);
+ FOR_EACH (StringVectCIter, it, tempList)
+ {
+ const std::string &str = *it;
+ if (findLast(str, ext))
+ list.push_back(str);
+ }
+ std::sort(list.begin(), list.end());
+ }
+
+ void getFiles(const std::string &path,
+ StringVect &list)
+ {
+ VirtList *const fonts = VirtFs::enumerateFiles(path);
+ FOR_EACH (StringVectCIter, i, fonts->names)
+ {
+ if (!VirtFs::isDirectory(path + dirSeparator + *i))
+ list.push_back(*i);
+ }
+ VirtFs::freeList(fonts);
+ }
+
+ void getDirs(const std::string &path, StringVect &list)
+ {
+ VirtList *const fonts = VirtFs::enumerateFiles(path);
+ FOR_EACH (StringVectCIter, i, fonts->names)
+ {
+ if (VirtFs::isDirectory(path + dirSeparator + *i))
+ list.push_back(*i);
+ }
+ VirtFs::freeList(fonts);
+ }
+
+ std::string getPath(const std::string &file)
+ {
+ // get the real path to the file
+ const std::string tmp = VirtFs::getRealDir(file);
+ std::string path;
+
+ // if the file is not in the search path, then its empty
+ if (!tmp.empty())
+ {
+ path = std::string(tmp).append(dirSeparator).append(file);
+#if defined __native_client__
+ std::string dataZip = "/http/data.zip/";
+ if (path.substr(0, dataZip.length()) == dataZip)
+ path = path.replace(0, dataZip.length(), "/http/data/");
+#endif // defined __native_client__
+ }
+ else
+ {
+ // if not found in search path return the default path
+ path = getPackageDir().append(dirSeparator).append(file);
+ }
+
+ return path;
+ }
+
+ std::string loadTextFileString(const std::string &fileName)
+ {
+ int contentsLength;
+ char *fileContents = static_cast<char*>(
+ VirtFs::loadFile(fileName, contentsLength));
+
+ if (!fileContents)
+ {
+ logger->log("Couldn't load text file: %s", fileName.c_str());
+ return std::string();
+ }
+ const std::string str = std::string(fileContents, contentsLength);
+ free(fileContents);
+ return str;
+ }
+
+ bool loadTextFile(const std::string &fileName,
+ StringVect &lines)
+ {
+ int contentsLength;
+ char *fileContents = static_cast<char*>(
+ VirtFs::loadFile(fileName, contentsLength));
+
+ if (!fileContents)
+ {
+ logger->log("Couldn't load text file: %s", fileName.c_str());
+ return false;
+ }
+
+ std::istringstream iss(std::string(fileContents, contentsLength));
+ std::string line;
+
+ while (getline(iss, line))
+ lines.push_back(line);
+
+ free(fileContents);
+ return true;
+ }
+} // namespace VirtFs
diff --git a/src/fs/virtfstools.h b/src/fs/virtfstools.h
new file mode 100644
index 000000000..4f2a77f4c
--- /dev/null
+++ b/src/fs/virtfstools.h
@@ -0,0 +1,54 @@
+/*
+ * 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_VIRTFSTOOLS_H
+#define UTILS_VIRTFSTOOLS_H
+
+#include "enums/simpletypes/append.h"
+
+#include "utils/stringvector.h"
+
+#include "localconsts.h"
+
+namespace VirtFs
+{
+ void *loadFile(const std::string &restrict fileName,
+ int &restrict fileSize);
+ void searchAndAddArchives(const std::string &restrict path,
+ const std::string &restrict ext,
+ const Append append);
+ void searchAndRemoveArchives(const std::string &restrict path,
+ const std::string &restrict ext);
+ void getFilesInDir(const std::string &dir,
+ StringVect &list,
+ const std::string &ext);
+ void getFilesWithDir(const std::string &restrict path,
+ StringVect &restrict list);
+ void getFiles(const std::string &path,
+ StringVect &list);
+ void getDirs(const std::string &path,
+ StringVect &list);
+ std::string getPath(const std::string &file);
+ bool loadTextFile(const std::string &fileName,
+ StringVect &lines);
+ std::string loadTextFileString(const std::string &fileName);
+} // namespace VirtFs
+
+#endif // UTILS_VIRTFSTOOLS_H
diff --git a/src/fs/virtlist.cpp b/src/fs/virtlist.cpp
new file mode 100644
index 000000000..d561e6b66
--- /dev/null
+++ b/src/fs/virtlist.cpp
@@ -0,0 +1,32 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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/virtlist.h"
+
+#include "debug.h"
+
+VirtList::VirtList() :
+ names()
+{
+}
+
+VirtList::~VirtList()
+{
+}
diff --git a/src/fs/virtlist.h b/src/fs/virtlist.h
new file mode 100644
index 000000000..5b1d10924
--- /dev/null
+++ b/src/fs/virtlist.h
@@ -0,0 +1,39 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 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_VIRTLIST_H
+#define UTILS_VIRTLIST_H
+
+#include "utils/stringvector.h"
+
+#include "localconsts.h"
+
+struct VirtList final
+{
+ VirtList();
+
+ A_DELETE_COPY(VirtList)
+
+ ~VirtList();
+
+ StringVect names;
+};
+
+#endif // UTILS_VIRTLIST_H