Otclient  14/8/2020
resourcemanager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "resourcemanager.h"
24 #include "filestream.h"
25 
29 
30 #include <physfs.h>
31 
33 
34 void ResourceManager::init(const char *argv0)
35 {
36  PHYSFS_init(argv0);
37  PHYSFS_permitSymbolicLinks(1);
38 }
39 
41 {
42  PHYSFS_deinit();
43 }
44 
45 bool ResourceManager::discoverWorkDir(const std::string& existentFile)
46 {
47  // search for modules directory
48  std::string possiblePaths[] = { g_platform.getCurrentDir(),
50  g_resources.getBaseDir() + "../",
51  g_resources.getBaseDir() + "../share/" + g_app.getCompactName() + "/" };
52 
53  bool found = false;
54  for(const std::string& dir : possiblePaths) {
55  if(!PHYSFS_mount(dir.c_str(), nullptr, 0))
56  continue;
57 
58  if(PHYSFS_exists(existentFile.c_str())) {
59  g_logger.debug(stdext::format("Found work dir at '%s'", dir));
60  m_workDir = dir;
61  found = true;
62  break;
63  }
64  PHYSFS_unmount(dir.c_str());
65  }
66 
67  return found;
68 }
69 
70 bool ResourceManager::setupUserWriteDir(const std::string& appWriteDirName)
71 {
72  std::string userDir = getUserDir();
73  std::string dirName;
74 #ifndef WIN32
75  dirName = stdext::format(".%s", appWriteDirName);
76 #else
77  dirName = appWriteDirName;
78 #endif
79  std::string writeDir = userDir + dirName;
80 
81  if(!PHYSFS_setWriteDir(writeDir.c_str())) {
82  if(!PHYSFS_setWriteDir(userDir.c_str()) || !PHYSFS_mkdir(dirName.c_str())) {
83  g_logger.error(stdext::format("Unable to create write directory '%s': %s", writeDir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
84  return false;
85  }
86  }
87  return setWriteDir(writeDir);
88 }
89 
90 bool ResourceManager::setWriteDir(const std::string& writeDir, bool create)
91 {
92  if(!PHYSFS_setWriteDir(writeDir.c_str())) {
93  g_logger.error(stdext::format("Unable to set write directory '%s': %s", writeDir, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
94  return false;
95  }
96 
97  if(!m_writeDir.empty())
98  removeSearchPath(m_writeDir);
99 
100  m_writeDir = writeDir;
101 
102  if(!addSearchPath(writeDir))
103  g_logger.error(stdext::format("Unable to add write '%s' directory to search path", writeDir));
104 
105  return true;
106 }
107 
108 bool ResourceManager::addSearchPath(const std::string& path, bool pushFront)
109 {
110  std::string savePath = path;
111  if(!PHYSFS_mount(path.c_str(), nullptr, pushFront ? 0 : 1)) {
112  bool found = false;
113  for(std::string searchPath : m_searchPaths) {
114  std::string newPath = searchPath + path;
115  if(PHYSFS_mount(newPath.c_str(), nullptr, pushFront ? 0 : 1)) {
116  savePath = newPath;
117  found = true;
118  break;
119  }
120  }
121 
122  if(!found) {
123  //g_logger.error(stdext::format("Could not add '%s' to directory search path. Reason %s", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
124  return false;
125  }
126  }
127  if(pushFront)
128  m_searchPaths.push_front(savePath);
129  else
130  m_searchPaths.push_back(savePath);
131  return true;
132 }
133 
134 bool ResourceManager::removeSearchPath(const std::string& path)
135 {
136  if(!PHYSFS_unmount(path.c_str()))
137  return false;
138  auto it = std::find(m_searchPaths.begin(), m_searchPaths.end(), path);
139  assert(it != m_searchPaths.end());
140  m_searchPaths.erase(it);
141  return true;
142 }
143 
144 void ResourceManager::searchAndAddPackages(const std::string& packagesDir, const std::string& packageExt)
145 {
146  auto files = listDirectoryFiles(packagesDir);
147  for(auto it = files.rbegin(); it != files.rend(); ++it) {
148  const std::string& file = *it;
149  if(!stdext::ends_with(file, packageExt))
150  continue;
151  std::string package = getRealDir(packagesDir) + "/" + file;
152  if(!addSearchPath(package, true))
153  g_logger.error(stdext::format("Unable to read package '%s': %s", package, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
154  }
155 }
156 
157 bool ResourceManager::fileExists(const std::string& fileName)
158 {
159  const std::string path = resolvePath(fileName);
160  return (PHYSFS_exists(path.c_str()) && !directoryExists(path));
161 }
162 
163 bool ResourceManager::directoryExists(const std::string& directoryName)
164 {
165  PHYSFS_Stat stat = {};
166  if (!PHYSFS_stat(directoryName.c_str(), &stat)) {
167  return false;
168  }
169 
170  return stat.filetype == PHYSFS_FILETYPE_DIRECTORY;
171 }
172 
173 void ResourceManager::readFileStream(const std::string& fileName, std::iostream& out)
174 {
175  std::string buffer = readFileContents(fileName);
176  if(buffer.length() == 0) {
177  out.clear(std::ios::eofbit);
178  return;
179  }
180  out.clear(std::ios::goodbit);
181  out.write(&buffer[0], buffer.length());
182  out.seekg(0, std::ios::beg);
183 }
184 
185 std::string ResourceManager::readFileContents(const std::string& fileName)
186 {
187  std::string fullPath = resolvePath(fileName);
188 
189  PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str());
190  if(!file)
191  stdext::throw_exception(stdext::format("unable to open file '%s': %s", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
192 
193  int fileSize = PHYSFS_fileLength(file);
194  std::string buffer(fileSize, 0);
195  PHYSFS_readBytes(file, (void*)&buffer[0], fileSize);
196  PHYSFS_close(file);
197 
198  return buffer;
199 }
200 
201 bool ResourceManager::writeFileBuffer(const std::string& fileName, const uchar* data, uint size)
202 {
203  PHYSFS_file* file = PHYSFS_openWrite(fileName.c_str());
204  if(!file) {
205  g_logger.error(PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
206  return false;
207  }
208 
209  PHYSFS_writeBytes(file, (void*)data, size);
210  PHYSFS_close(file);
211  return true;
212 }
213 
214 bool ResourceManager::writeFileStream(const std::string& fileName, std::iostream& in)
215 {
216  std::streampos oldPos = in.tellg();
217  in.seekg(0, std::ios::end);
218  std::streampos size = in.tellg();
219  in.seekg(0, std::ios::beg);
220  std::vector<char> buffer(size);
221  in.read(&buffer[0], size);
222  bool ret = writeFileBuffer(fileName, (const uchar*)&buffer[0], size);
223  in.seekg(oldPos, std::ios::beg);
224  return ret;
225 }
226 
227 bool ResourceManager::writeFileContents(const std::string& fileName, const std::string& data)
228 {
229  return writeFileBuffer(fileName, (const uchar*)data.c_str(), data.size());
230 }
231 
232 FileStreamPtr ResourceManager::openFile(const std::string& fileName)
233 {
234  std::string fullPath = resolvePath(fileName);
235 
236  PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str());
237  if(!file)
238  stdext::throw_exception(stdext::format("unable to open file '%s': %s", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
239  return FileStreamPtr(new FileStream(fullPath, file, false));
240 }
241 
242 FileStreamPtr ResourceManager::appendFile(const std::string& fileName)
243 {
244  PHYSFS_File* file = PHYSFS_openAppend(fileName.c_str());
245  if(!file)
246  stdext::throw_exception(stdext::format("failed to append file '%s': %s", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
247  return FileStreamPtr(new FileStream(fileName, file, true));
248 }
249 
250 FileStreamPtr ResourceManager::createFile(const std::string& fileName)
251 {
252  PHYSFS_File* file = PHYSFS_openWrite(fileName.c_str());
253  if(!file)
254  stdext::throw_exception(stdext::format("failed to create file '%s': %s", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
255  return FileStreamPtr(new FileStream(fileName, file, true));
256 }
257 
258 bool ResourceManager::deleteFile(const std::string& fileName)
259 {
260  return PHYSFS_delete(resolvePath(fileName).c_str()) != 0;
261 }
262 
263 bool ResourceManager::makeDir(const std::string directory)
264 {
265  return PHYSFS_mkdir(directory.c_str());
266 }
267 
268 std::list<std::string> ResourceManager::listDirectoryFiles(const std::string& directoryPath)
269 {
270  std::list<std::string> files;
271  auto rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str());
272 
273  for(int i = 0; rc[i] != nullptr; i++)
274  files.emplace_back(rc[i]);
275 
276  PHYSFS_freeList(rc);
277  return files;
278 }
279 
280 std::vector<std::string> ResourceManager::getDirectoryFiles(const std::string& path, bool filenameOnly, bool recursive)
281 {
282  if(!fs::exists(path))
283  return std::vector<std::string>();
284 
285  fs::path p(path);
286  return discoverPath(p, filenameOnly, recursive);
287 }
288 
289 std::vector<std::string> ResourceManager::discoverPath(const fs::path& path, bool filenameOnly, bool recursive)
290 {
291  std::vector<std::string> files;
292 
293  /* Before doing anything, we have to add this directory to search path,
294  * this is needed so it works correctly when one wants to open a file. */
295  addSearchPath(path.generic_string(), true);
296  for (fs::directory_iterator it(path), end; it != end; ++it) {
297  if(fs::is_directory(it->path().generic_string()) && recursive) {
298  std::vector<std::string> subfiles = discoverPath(it->path(), filenameOnly, recursive);
299  files.insert(files.end(), subfiles.begin(), subfiles.end());
300  } else {
301  if(filenameOnly)
302  files.push_back(it->path().filename().string());
303  else
304  files.push_back(it->path().generic_string() + "/" + it->path().filename().string());
305  }
306  }
307 
308  return files;
309 }
310 
311 std::string ResourceManager::resolvePath(const std::string& path)
312 {
313  std::string fullPath;
314  if(stdext::starts_with(path, "/"))
315  fullPath = path;
316  else {
317  std::string scriptPath = "/" + g_lua.getCurrentSourcePath();
318  if(!scriptPath.empty())
319  fullPath += scriptPath + "/";
320  fullPath += path;
321  }
322  if(!(stdext::starts_with(fullPath, "/")))
323  g_logger.traceWarning(stdext::format("the following file path is not fully resolved: %s", path));
324  stdext::replace_all(fullPath, "//", "/");
325  return fullPath;
326 }
327 
328 std::string ResourceManager::getRealDir(const std::string& path)
329 {
330  std::string dir;
331  const char *cdir = PHYSFS_getRealDir(resolvePath(path).c_str());
332  if(cdir)
333  dir = cdir;
334  return dir;
335 }
336 
337 std::string ResourceManager::getRealPath(const std::string& path)
338 {
339  return getRealDir(path) + "/" + path;
340 }
341 
343 {
344  return PHYSFS_getBaseDir();
345 }
346 
348 {
349  return PHYSFS_getUserDir();
350 }
351 
352 std::string ResourceManager::guessFilePath(const std::string& filename, const std::string& type)
353 {
354  if(isFileType(filename, type))
355  return filename;
356  return filename + "." + type;
357 }
358 
359 bool ResourceManager::isFileType(const std::string& filename, const std::string& type)
360 {
361  if(stdext::ends_with(filename, std::string(".") + type))
362  return true;
363  return false;
364 }
365 
366 ticks_t ResourceManager::getFileTime(const std::string& filename)
367 {
369 }
ResourceManager::isFileType
bool isFileType(const std::string &filename, const std::string &type)
Definition: resourcemanager.cpp:359
ResourceManager::listDirectoryFiles
std::list< std::string > listDirectoryFiles(const std::string &directoryPath="")
Definition: resourcemanager.cpp:268
ResourceManager::writeFileStream
bool writeFileStream(const std::string &fileName, std::iostream &in)
Definition: resourcemanager.cpp:214
Platform::getCurrentDir
std::string getCurrentDir()
Definition: unixplatform.cpp:83
ResourceManager::removeSearchPath
bool removeSearchPath(const std::string &path)
Definition: resourcemanager.cpp:134
ResourceManager::getRealPath
std::string getRealPath(const std::string &path)
Definition: resourcemanager.cpp:337
ResourceManager::getRealDir
std::string getRealDir(const std::string &path)
Definition: resourcemanager.cpp:328
Application::getCompactName
const std::string & getCompactName()
Definition: application.h:52
ResourceManager::directoryExists
bool directoryExists(const std::string &directoryName)
Definition: resourcemanager.cpp:163
ResourceManager::appendFile
FileStreamPtr appendFile(const std::string &fileName)
Definition: resourcemanager.cpp:242
resourcemanager.h
ResourceManager::resolvePath
std::string resolvePath(const std::string &path)
Definition: resourcemanager.cpp:311
luainterface.h
Logger::error
void error(const std::string &what)
Definition: logger.h:54
ticks_t
int64 ticks_t
Definition: types.h:43
ResourceManager::getDirectoryFiles
std::vector< std::string > getDirectoryFiles(const std::string &path, bool filenameOnly, bool recursive)
Definition: resourcemanager.cpp:280
FileStreamPtr
stdext::shared_object_ptr< FileStream > FileStreamPtr
Definition: declarations.h:43
ResourceManager
Definition: resourcemanager.h:33
ResourceManager::init
void init(const char *argv0)
Definition: resourcemanager.cpp:34
stdext::format
std::string format()
Definition: format.h:82
stdext::starts_with
bool starts_with(const std::string &str, const std::string &test)
Definition: string.cpp:263
ResourceManager::setupUserWriteDir
bool setupUserWriteDir(const std::string &appWriteDirName)
Definition: resourcemanager.cpp:70
ResourceManager::getFileTime
ticks_t getFileTime(const std::string &filename)
Definition: resourcemanager.cpp:366
g_resources
ResourceManager g_resources
Definition: resourcemanager.cpp:32
ResourceManager::fileExists
bool fileExists(const std::string &fileName)
Definition: resourcemanager.cpp:157
Platform::getFileModificationTime
ticks_t getFileModificationTime(std::string file)
Definition: unixplatform.cpp:112
uint
unsigned int uint
Definition: types.h:31
ResourceManager::searchAndAddPackages
void searchAndAddPackages(const std::string &packagesDir, const std::string &packageExt)
Definition: resourcemanager.cpp:144
g_logger
Logger g_logger
Definition: logger.cpp:35
g_lua
LuaInterface g_lua
Definition: luainterface.cpp:31
ResourceManager::terminate
void terminate()
Definition: resourcemanager.cpp:40
FileStream
Definition: filestream.h:34
ResourceManager::makeDir
bool makeDir(const std::string directory)
Definition: resourcemanager.cpp:263
platform.h
g_app
ConsoleApplication g_app
Definition: consoleapplication.cpp:32
LuaInterface::getCurrentSourcePath
std::string getCurrentSourcePath(int level=0)
Searches for the source of the current running function.
Definition: luainterface.cpp:405
stdext::throw_exception
void throw_exception(const std::string &what)
Throws a generic exception.
Definition: exception.h:43
stdext::ends_with
bool ends_with(const std::string &str, const std::string &test)
Definition: string.cpp:258
ResourceManager::openFile
FileStreamPtr openFile(const std::string &fileName)
Definition: resourcemanager.cpp:232
ResourceManager::readFileContents
std::string readFileContents(const std::string &fileName)
Definition: resourcemanager.cpp:185
ResourceManager::addSearchPath
bool addSearchPath(const std::string &path, bool pushFront=false)
Definition: resourcemanager.cpp:108
filestream.h
ResourceManager::deleteFile
bool deleteFile(const std::string &fileName)
Definition: resourcemanager.cpp:258
uchar
unsigned char uchar
Definition: types.h:29
Logger::debug
void debug(const std::string &what)
Definition: logger.h:51
ResourceManager::readFileStream
void readFileStream(const std::string &fileName, std::iostream &out)
Definition: resourcemanager.cpp:173
stdext::shared_object_ptr< FileStream >
stdext::replace_all
void replace_all(std::string &str, const std::string &search, const std::string &replacement)
Definition: string.cpp:268
ResourceManager::discoverPath
std::vector< std::string > discoverPath(const fs::path &path, bool filenameOnly, bool recursive)
Definition: resourcemanager.cpp:289
ResourceManager::writeFileContents
bool writeFileContents(const std::string &fileName, const std::string &data)
Definition: resourcemanager.cpp:227
ResourceManager::setWriteDir
bool setWriteDir(const std::string &writeDir, bool create=false)
Definition: resourcemanager.cpp:90
ResourceManager::writeFileBuffer
bool writeFileBuffer(const std::string &fileName, const uchar *data, uint size)
Definition: resourcemanager.cpp:201
ResourceManager::createFile
FileStreamPtr createFile(const std::string &fileName)
Definition: resourcemanager.cpp:250
ResourceManager::discoverWorkDir
bool discoverWorkDir(const std::string &existentFile)
Definition: resourcemanager.cpp:45
ResourceManager::getBaseDir
std::string getBaseDir()
Definition: resourcemanager.cpp:342
ResourceManager::guessFilePath
std::string guessFilePath(const std::string &filename, const std::string &type)
Definition: resourcemanager.cpp:352
ResourceManager::getUserDir
std::string getUserDir()
Definition: resourcemanager.cpp:347
application.h
g_platform
Platform g_platform
Definition: platform.cpp:25