ManaPlus
dirs.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "dirs.h"
25 
26 #include "client.h"
27 #include "configuration.h"
28 #include "logger.h"
29 #include "main.h"
30 #include "settings.h"
31 
32 #include "fs/mkdir.h"
33 #include "fs/paths.h"
34 
35 #include "fs/virtfs/fs.h"
36 
37 #include "utils/base64.h"
38 #if defined(__native_client__) || (defined(ANDROID) && defined(USE_SDL2))
39 #include "fs/files.h"
40 #endif // defined(__native_client__) || (defined(ANDROID) &&
41  // defined(USE_SDL2))
42 
43 #include "utils/cast.h"
44 #include "utils/gettext.h"
45 
46 #ifdef ANDROID
47 #ifdef USE_SDL2
48 #include "main.h"
49 
50 #include "render/graphics.h"
51 #endif // USE_SDL2
52 #endif // ANDROID
53 
54 #ifdef __APPLE__
55 #include <CoreFoundation/CFBundle.h>
56 #endif // __APPLE__
57 
58 #ifdef WIN32
59 PRAGMA48(GCC diagnostic push)
60 PRAGMA48(GCC diagnostic ignored "-Wshadow")
61 #include <SDL_syswm.h>
62 PRAGMA48(GCC diagnostic pop)
63 #include "fs/specialfolder.h"
64 #undef ERROR
65 #endif // WIN32
66 
67 #include <sys/stat.h>
68 
69 #include <sstream>
70 
71 #include "debug.h"
72 
73 #if defined __native_client__
74 #define _nacl_dir std::string("/persistent/manaplus")
75 #endif // defined __native_client__
76 
77 #ifdef ANDROID
78 #ifdef USE_SDL2
79 
80 int loadingProgressCounter = 1;
81 
82 static void updateProgress(int cnt)
83 {
84  const int progress = cnt + loadingProgressCounter;
85  const int h = mainGraphics->mHeight;
86  mainGraphics->setColor(Color(255, 255, 255, 255));
87  const int maxSize = mainGraphics->mWidth - 100;
88  const int width = maxSize * progress / 50;
89  mainGraphics->fillRectangle(Rect(50, h - 100, width, 50));
91 }
92 
93 void Dirs::setProgress()
94 {
95  loadingProgressCounter++;
96  updateProgress(loadingProgressCounter);
97 }
98 
99 static void resetProgress()
100 {
101  loadingProgressCounter = 0;
102  updateProgress(loadingProgressCounter);
103 }
104 
105 void extractAssets()
106 {
107  if (!getenv("APPDIR"))
108  {
109  logger->log("error: APPDIR is not set!");
110  return;
111  }
112  const std::string fileName = pathJoin(getenv("APPDIR"),
113  "data.zip");
114  logger->log("Extracting asset into: " + fileName);
115  uint8_t *buf = new uint8_t[1000000];
116 
117  FILE *const file = fopen(fileName.c_str(), "w");
118  for (int f = 0; f < 100; f ++)
119  {
120  std::string part = strprintf("manaplus-data.zip%u%u",
121  CAST_U32(f / 10),
122  CAST_U32(f % 10));
123  logger->log("testing asset: " + part);
124  SDL_RWops *const rw = SDL_RWFromFile(part.c_str(), "r");
125  if (rw)
126  {
127  const int size = SDL_RWsize(rw);
128  int size2 = SDL_RWread(rw, buf, 1, size);
129  logger->log("asset size: %d", size2);
130  fwrite(buf, 1, size2, file);
131  SDL_RWclose(rw);
132  Dirs::setProgress();
133  }
134  else
135  {
136  break;
137  }
138  }
139  fclose(file);
140 
141  const std::string fileName2 = pathJoin(getenv("APPDIR"),
142  "locale.zip");
143  FILE *const file2 = fopen(fileName2.c_str(), "w");
144  SDL_RWops *const rw = SDL_RWFromFile("manaplus-locale.zip", "r");
145  if (rw)
146  {
147  const int size = SDL_RWsize(rw);
148  int size2 = SDL_RWread(rw, buf, 1, size);
149  fwrite(buf, 1, size2, file2);
150  SDL_RWclose(rw);
151  Dirs::setProgress();
152  }
153  fclose(file2);
154 
155  delete [] buf;
156 }
157 
158 #endif // USE_SDL2
159 #endif // ANDROID
160 
162 {
163  if (settings.options.dataPath.empty()
164  && !branding.getStringValue("dataPath").empty())
165  {
166  if (isRealPath(branding.getStringValue("dataPath")))
167  {
169  }
170  else
171  {
173  branding.getStringValue("dataPath"));
174  }
175  settings.options.skipUpdate = true;
176  }
177 }
178 
180 {
181 #if defined(ANDROID) && defined(USE_SDL2)
182  Files::setCopyCallBack(&updateProgress);
183  resetProgress();
184  extractAssets();
185 
186  const std::string zipName = pathJoin(getenv("APPDIR"), "data.zip");
187  const std::string dirName = pathJoin(getenv("APPDIR"), "data");
188  VirtFs::mountZip2(zipName,
189  "data",
190  Append_false);
191  VirtFs::mountZip2(zipName,
192  "data/perserver/default",
193  Append_false);
194  Files::extractLocale();
195 #endif // defined(ANDROID) && defined(USE_SDL2)
196 }
197 
199 {
200  VirtFs::mountDirSilent(PKG_DATADIR "data/perserver/default",
201  Append_false);
202  VirtFs::mountDirSilent("data/perserver/default",
203  Append_false);
204 
205 #if defined __APPLE__
206  CFBundleRef mainBundle = CFBundleGetMainBundle();
207  CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
208  char path[PATH_MAX];
209  if (!CFURLGetFileSystemRepresentation(resourcesURL,
210  TRUE,
211  reinterpret_cast<uint8_t*>(path),
212  PATH_MAX))
213  {
214  fprintf(stderr, "Can't find Resources directory\n");
215  }
216  CFRelease(resourcesURL);
217  std::string path2 = pathJoin(path, "data");
218  VirtFs::mountDir(pathJoin(path2, "perserver/default"), Append_false);
220 // possible this need for support run client from dmg images.
221 // mPackageDir = path;
222 #endif // defined __APPLE__
223 
225  setPackageDir(PKG_DATADIR "data");
227 
228 #ifdef ANDROID
229 #ifdef USE_SDL2
230  if (getenv("APPDIR"))
231  {
232  const std::string appDir = getenv("APPDIR");
233  VirtFs::mountDir(appDir + "/data", Append_false);
234  VirtFs::mountDir(appDir + "/data/perserver/default",
235  Append_false);
236  }
237 #endif // USE_SDL2
238 #endif // ANDROID
239 
240 #if defined __native_client__
241  VirtFs::mountZip("/http/data.zip", Append_false);
242  VirtFs::mountZip2("/http/data.zip",
243  "perserver/default",
244  Append_false);
245 #endif // defined __native_client__
246 
247 #ifndef WIN32
248  // Add branding/data to VirtFS search path
249  if (!settings.options.brandingPath.empty())
250  {
251  std::string path = settings.options.brandingPath;
252 
253  // Strip blah.manaplus from the path
254  const int loc = CAST_S32(path.find_last_of('/'));
255 
256  if (loc > 0)
257  {
258  VirtFs::mountDir(path.substr(
259  0, loc + 1).append("data"),
260  Append_false);
261  }
262  }
263 #endif // WIN32
264 }
265 
267 {
269  const std::string portableName = settings.rootDir + "portable.xml";
270  struct stat statbuf;
271 
272  if (stat(portableName.c_str(), &statbuf) == 0 &&
273  S_ISREG(statbuf.st_mode))
274  {
275  std::string dir;
276  Configuration portable;
277  portable.init(portableName,
280 
281  if (settings.options.brandingPath.empty())
282  {
283  branding.init(portableName,
287  }
288 
289  logger->log("Portable file: %s", portableName.c_str());
290 
291  if (settings.options.localDataDir.empty())
292  {
293  dir = portable.getValue("dataDir", "");
294  if (!dir.empty())
295  {
297  logger->log("Portable data dir: %s",
298  settings.options.localDataDir.c_str());
299  }
300  }
301 
302  if (settings.options.configDir.empty())
303  {
304  dir = portable.getValue("configDir", "");
305  if (!dir.empty())
306  {
308  logger->log("Portable config dir: %s",
309  settings.options.configDir.c_str());
310  }
311  }
312 
313  if (settings.options.screenshotDir.empty())
314  {
315  dir = portable.getValue("screenshotDir", "");
316  if (!dir.empty())
317  {
319  logger->log("Portable screenshot dir: %s",
320  settings.options.screenshotDir.c_str());
321  }
322  }
323  }
324 }
325 
331 {
333  initTempDir();
334  initConfigDir();
335 }
336 
338 {
340 
341  if (settings.localDataDir.empty())
342  {
343 #ifdef __APPLE__
344  // Use Application Directory instead of .mana
346  "Library/Application Support",
347  branding.getValue("appName", "ManaPlus"));
348 #elif defined __HAIKU__
350  "config/cache/Mana");
351 #elif defined WIN32
352  settings.localDataDir = getSpecialFolderLocation(CSIDL_LOCAL_APPDATA);
353  if (settings.localDataDir.empty())
356  "Mana");
357 #elif defined __ANDROID__
358  settings.localDataDir = pathJoin(getSdStoragePath(),
359  branding.getValue("appShort", "ManaPlus"),
360  "local");
361 #elif defined __native_client__
362  settings.localDataDir = pathJoin(_nacl_dir, "local");
363 #elif defined __SWITCH__
365 #else // __APPLE__
366 
368  ".local/share/mana");
369 #endif // __APPLE__
370  }
371 
372  if (mkdir_r(settings.localDataDir.c_str()) != 0)
373  {
374  // TRANSLATORS: directory creation error
375  logger->error(strprintf(_("%s doesn't exist and can't be created! "
376  "Exiting."), settings.localDataDir.c_str()));
377  }
378 #ifdef USE_PROFILER
380 #endif // USE_PROFILER
381 }
382 
384 {
386 
387  if (mkdir_r(settings.tempDir.c_str()) != 0)
388  {
389  // TRANSLATORS: directory creation error
390  logger->error(strprintf(_("%s doesn't exist and can't be created! "
391  "Exiting."), settings.tempDir.c_str()));
392  }
393 // ResourceManager::deleteFilesInDirectory(settings.tempDir);
394 }
395 
397 {
399 
400  if (settings.configDir.empty())
401  {
402 #ifdef __APPLE__
404  branding.getValue("appShort", "mana"));
405 #elif defined __HAIKU__
407  "config/settings/Mana",
408  branding.getValue("appName", "ManaPlus"));
409 #elif defined WIN32
410  settings.configDir = getSpecialFolderLocation(CSIDL_APPDATA);
411  if (settings.configDir.empty())
412  {
414  }
415  else
416  {
418  "mana",
419  branding.getValue("appShort", "mana"));
420  }
421 #elif defined __ANDROID__
422  settings.configDir = pathJoin(getSdStoragePath(),
423  branding.getValue("appShort", "ManaPlus"),
424  "config");
425 #elif defined __native_client__
426  settings.configDir = pathJoin(_nacl_dir, "config");
427 #elif defined __SWITCH__
429 #else // __APPLE__
430 
432  ".config/mana",
433  branding.getValue("appShort", "mana"));
434 #endif // __APPLE__
435 
436  logger->log("Generating config dir: " + settings.configDir);
437  }
438 
439  if (mkdir_r(settings.configDir.c_str()) != 0)
440  {
441  // TRANSLATORS: directory creation error
442  logger->error(strprintf(_("%s doesn't exist and can't be created! "
443  "Exiting."), settings.configDir.c_str()));
444  }
445 }
446 
452 {
453  std::stringstream updates;
454 
455  // If updatesHost is currently empty, fill it from config file
456  if (settings.updateHost.empty())
457  settings.updateHost = config.getStringValue("updatehost");
459  return;
460 
461  // Don't go out of range int he next check
462  if (settings.updateHost.length() < 2)
463  {
464  if (settings.updatesDir.empty())
466  return;
467  }
468 
469  const size_t sz = settings.updateHost.size();
470  // Remove any trailing slash at the end of the update host
471  if (settings.updateHost.at(sz - 1) == '/')
472  settings.updateHost.resize(sz - 1);
473 
474  // Parse out any "http://" or "https://", and set the updates directory
475  const size_t pos = settings.updateHost.find("://");
476  if (pos != std::string::npos)
477  {
478  if (pos + 3 < settings.updateHost.length()
479  && !settings.updateHost.empty())
480  {
481  updates << "updates/" << settings.updateHost.substr(pos + 3);
482  settings.updatesDir = updates.str();
483  }
484  else
485  {
486  logger->log("Error: Invalid update host: %s",
487  settings.updateHost.c_str());
488  // TRANSLATORS: update server initialisation error
489  errorMessage = strprintf(_("Invalid update host: %s."),
490  settings.updateHost.c_str());
492  }
493  }
494  else
495  {
496  logger->log1("Warning: no protocol was specified for the update host");
497  updates << "updates/" << settings.updateHost;
498  settings.updatesDir = updates.str();
499  }
500 
501 #ifdef WIN32
502  if (settings.updatesDir.find(":") != std::string::npos)
503  replaceAll(settings.updatesDir, ":", "_");
504 #endif // WIN32
505 
506  const std::string updateDir("/" + settings.updatesDir);
507 
508  // Verify that the updates directory exists. Create if necessary.
509  if (!VirtFs::isDirectory(updateDir))
510  {
511  if (!VirtFs::mkdir(updateDir))
512  {
513 #if defined WIN32
514  std::string newDir = pathJoin(settings.localDataDir,
516  if (!CreateDirectory(newDir.c_str(), nullptr) &&
517  GetLastError() != ERROR_ALREADY_EXISTS)
518  {
519  logger->log("Error: %s can't be made, but doesn't exist!",
520  newDir.c_str());
521  // TRANSLATORS: update server initialisation error
522  errorMessage = _("Error creating updates directory!");
524  }
525 #else // defined WIN32
526 
527  logger->log("Error: %s/%s can't be made, but doesn't exist!",
528  settings.localDataDir.c_str(),
529  settings.updatesDir.c_str());
530  // TRANSLATORS: update server initialisation error
531  errorMessage = _("Error creating updates directory!");
533 #endif // defined WIN32
534  }
535  }
536  const std::string updateLocal = pathJoin(updateDir, "local");
537  const std::string updateFix = pathJoin(updateDir, "fix");
538  if (!VirtFs::isDirectory(updateLocal))
539  VirtFs::mkdir(updateLocal);
540  if (!VirtFs::isDirectory(updateFix))
541  VirtFs::mkdir(updateFix);
542 }
543 
545 {
546  if (!settings.options.screenshotDir.empty())
547  {
549  if (mkdir_r(settings.screenshotDir.c_str()) != 0)
550  {
552  // TRANSLATORS: directory creation error
553  _("Error: %s doesn't exist and can't be created! "
554  "Exiting."), settings.screenshotDir.c_str()));
555  }
556  }
557  else if (settings.screenshotDir.empty())
558  {
559 #ifdef __native_client__
560  settings.screenshotDir = pathJoin(_nacl_dir, "screenshots/");
561 #else // __native_client__
563  config.getStringValue("screenshotDirectory3"));
564  if (settings.screenshotDir.empty())
565  {
566 #ifdef __ANDROID__
567  settings.screenshotDir = getSdStoragePath()
568  + std::string("/images");
569 
570  if (mkdir_r(settings.screenshotDir.c_str()))
571  {
573  // TRANSLATORS: directory creation error
574  _("Error: %s doesn't exist and can't be created! "
575  "Exiting."), settings.screenshotDir.c_str()));
576  }
577 #else // ANDROID
579 #endif // ANDROID
580  if (config.getBoolValue("useScreenshotDirectorySuffix"))
581  {
582  const std::string configScreenshotSuffix =
583  branding.getValue("screenshots", "ManaPlus");
584 
585  if (!configScreenshotSuffix.empty())
586  {
588  configScreenshotSuffix);
589  }
590  }
591  config.setValue("screenshotDirectory3",
593  }
594 #endif // __native_client__
595  }
596  logger->log("screenshotDirectory: " + settings.screenshotDir);
597 }
598 
600 {
602  if (mkdir_r(settings.usersDir.c_str()) != 0)
603  {
604  // TRANSLATORS: directory creation error
605  logger->error(strprintf(_("%s doesn't exist and can't be created!"),
606  settings.usersDir.c_str()));
607  }
608 
610  if (mkdir_r(settings.npcsDir.c_str()) != 0)
611  {
612  // TRANSLATORS: directory creation error
613  logger->error(strprintf(_("%s doesn't exist and can't be created!"),
614  settings.npcsDir.c_str()));
615  }
616 
618  if (mkdir_r(settings.usersIdDir.c_str()) != 0)
619  {
620  // TRANSLATORS: directory creation error
621  logger->error(strprintf(_("%s doesn't exist and can't be created!"),
622  settings.usersIdDir.c_str()));
623  }
624 }
const bool Append_false
Definition: append.h:30
std::string decodeBase64String(std::string value)
Definition: base64.cpp:203
std::string encodeBase64String(std::string value)
Definition: base64.cpp:188
#define CAST_S32
Definition: cast.h:30
#define CAST_U32
Definition: cast.h:31
void setState(const StateT state)
Definition: client.h:66
Definition: color.h:76
std::string getValue(const std::string &key, const std::string &deflt) const
bool getBoolValue(const std::string &key) const
std::string getStringValue(const std::string &key) const
void setValue(const std::string &key, const std::string &value)
void init(const std::string &filename, const UseVirtFs useResManager, const SkipError skipError)
std::string getDirectory() const
static void initRootDir()
Definition: dirs.cpp:266
static void initTempDir()
Definition: dirs.cpp:383
static void initConfigDir()
Definition: dirs.cpp:396
static void initUpdatesDir()
Definition: dirs.cpp:451
static void initHomeDir()
Definition: dirs.cpp:330
static void initLocalDataDir()
Definition: dirs.cpp:337
static void initUsersDir()
Definition: dirs.cpp:599
static void initScreenshotDir()
Definition: dirs.cpp:544
static void extractDataDir()
Definition: dirs.cpp:179
static void mountDataDir()
Definition: dirs.cpp:198
static void updateDataPath()
Definition: dirs.cpp:161
virtual void updateScreen()=0
int mWidth
Definition: graphics.h:484
virtual void fillRectangle(const Rect &rectangle)=0
int mHeight
Definition: graphics.h:485
virtual void setColor(const Color &color)
Definition: graphics.h:320
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
void error(const std::string &error_text) __attribute__((noreturn))
Definition: logger.cpp:472
Definition: rect.h:74
std::string tempDir
Definition: settings.h:113
std::string localDataDir
Definition: settings.h:112
std::string rootDir
Definition: settings.h:123
std::string serverName
Definition: settings.h:114
Options options
Definition: settings.h:130
std::string usersIdDir
Definition: settings.h:119
std::string updatesDir
Definition: settings.h:110
std::string configDir
Definition: settings.h:111
std::string screenshotDir
Definition: settings.h:115
std::string serverConfigDir
Definition: settings.h:117
std::string updateHost
Definition: settings.h:107
std::string npcsDir
Definition: settings.h:120
std::string usersDir
Definition: settings.h:118
Configuration config
Configuration branding
void setBrandingDefaults(Configuration &cfg)
Definition: defaults.cpp:490
Client * client
Definition: client.cpp:118
std::string errorMessage
Definition: client.cpp:116
#define _(s)
Definition: gettext.h:35
Graphics * mainGraphics
Definition: graphics.cpp:109
#define PRAGMA48(str)
Definition: localconsts.h:199
Logger * logger
Definition: logger.cpp:89
#define PKG_DATADIR
Definition: main.h:178
int mkdir_r(const char *const pathname)
Create a directory, making leading components first if necessary.
Definition: mkdir.cpp:109
int size()
Definition: emotedb.cpp:306
void init()
Definition: playerinfo.cpp:434
@ ERROR
Definition: state.h:35
bool mkdir(const std::string &dirname)
Definition: fs.cpp:775
const char * getBaseDir()
Definition: fs.cpp:79
bool mountZip(std::string newDir, const Append append)
Definition: fs.cpp:590
bool isDirectory(std::string name)
Definition: fs.cpp:239
const char * getUserDir()
Definition: fs.cpp:84
bool mountDir(std::string newDir, const Append append)
Definition: fs.cpp:393
bool mountDirSilent(std::string newDir, const Append append)
Definition: fs.cpp:421
bool mountZip2(std::string newDir, std::string subDir, const Append append)
Definition: fs.cpp:626
std::string getPicturesDir()
Definition: paths.cpp:207
bool checkPath(const std::string &path)
Definition: paths.cpp:121
void setPackageDir(const std::string &dir)
Definition: paths.cpp:298
bool isRealPath(const std::string &str)
Definition: paths.cpp:116
Settings settings
Definition: settings.cpp:32
const bool SkipError_false
Definition: skiperror.h:30
std::string & replaceAll(std::string &context, const std::string &from, const std::string &to)
std::string strprintf(const char *const format,...)
std::string pathJoin(std::string str1, const std::string &str2)
std::string brandingPath
Definition: options.h:79
std::string screenshotDir
Definition: options.h:87
bool skipUpdate
Definition: options.h:95
std::string configDir
Definition: options.h:85
std::string localDataDir
Definition: options.h:86
std::string dataPath
Definition: options.h:81
std::string fileName
Definition: testmain.cpp:39
const bool UseVirtFs_false
Definition: usevirtfs.h:30