OpenTTD Source  14.1
fileio.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD 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, version 2.
4  * OpenTTD 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "fileio_func.h"
13 #include "debug.h"
14 #include "fios.h"
15 #include "string_func.h"
16 #include "tar_type.h"
17 #ifdef _WIN32
18 #include <windows.h>
19 # define access _taccess
20 #elif defined(__HAIKU__)
21 #include <Path.h>
22 #include <storage/FindDirectory.h>
23 #else
24 #include <unistd.h>
25 #include <pwd.h>
26 #endif
27 #include <charconv>
28 #include <sys/stat.h>
29 #include <sstream>
30 #include <filesystem>
31 
32 #include "safeguards.h"
33 
35 static bool _do_scan_working_directory = true;
36 
37 extern std::string _config_file;
38 extern std::string _highscore_file;
39 
40 static const char * const _subdirs[] = {
41  "",
42  "save" PATHSEP,
43  "save" PATHSEP "autosave" PATHSEP,
44  "scenario" PATHSEP,
45  "scenario" PATHSEP "heightmap" PATHSEP,
46  "gm" PATHSEP,
47  "data" PATHSEP,
48  "baseset" PATHSEP,
49  "newgrf" PATHSEP,
50  "lang" PATHSEP,
51  "ai" PATHSEP,
52  "ai" PATHSEP "library" PATHSEP,
53  "game" PATHSEP,
54  "game" PATHSEP "library" PATHSEP,
55  "screenshot" PATHSEP,
56  "social_integration" PATHSEP,
57 };
58 static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
59 
66 std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
67 std::vector<Searchpath> _valid_searchpaths;
68 std::array<TarList, NUM_SUBDIRS> _tar_list;
69 TarFileList _tar_filelist[NUM_SUBDIRS];
70 
71 typedef std::map<std::string, std::string> TarLinkList;
72 static TarLinkList _tar_linklist[NUM_SUBDIRS];
73 
74 extern bool FiosIsValidFile(const std::string &path, const struct dirent *ent, struct stat *sb);
75 
82 {
83  return sp < _searchpaths.size() && !_searchpaths[sp].empty();
84 }
85 
86 static void FillValidSearchPaths(bool only_local_path)
87 {
88  _valid_searchpaths.clear();
89 
90  std::set<std::string> seen{};
91  for (Searchpath sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) {
92  if (sp == SP_WORKING_DIR) continue;
93 
94  if (only_local_path) {
95  switch (sp) {
96  case SP_WORKING_DIR: // Can be influence by "-c" option.
97  case SP_BINARY_DIR: // Most likely contains all the language files.
98  case SP_AUTODOWNLOAD_DIR: // Otherwise we cannot download in-game content.
99  break;
100 
101  default:
102  continue;
103  }
104  }
105 
106  if (IsValidSearchPath(sp)) {
107  if (seen.count(_searchpaths[sp]) != 0) continue;
108  seen.insert(_searchpaths[sp]);
109  _valid_searchpaths.emplace_back(sp);
110  }
111  }
112 
113  /* The working-directory is special, as it is controlled by _do_scan_working_directory.
114  * Only add the search path if it isn't already in the set. To preserve the same order
115  * as the enum, insert it in the front. */
116  if (IsValidSearchPath(SP_WORKING_DIR) && seen.count(_searchpaths[SP_WORKING_DIR]) == 0) {
117  _valid_searchpaths.insert(_valid_searchpaths.begin(), SP_WORKING_DIR);
118  }
119 }
120 
127 bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
128 {
129  FILE *f = FioFOpenFile(filename, "rb", subdir);
130  if (f == nullptr) return false;
131 
132  FioFCloseFile(f);
133  return true;
134 }
135 
141 bool FileExists(const std::string &filename)
142 {
143  return access(OTTD2FS(filename).c_str(), 0) == 0;
144 }
145 
149 void FioFCloseFile(FILE *f)
150 {
151  fclose(f);
152 }
153 
160 std::string FioFindFullPath(Subdirectory subdir, const std::string &filename)
161 {
162  assert(subdir < NUM_SUBDIRS);
163 
164  for (Searchpath sp : _valid_searchpaths) {
165  std::string buf = FioGetDirectory(sp, subdir);
166  buf += filename;
167  if (FileExists(buf)) return buf;
168 #if !defined(_WIN32)
169  /* Be, as opening files, aware that sometimes the filename
170  * might be in uppercase when it is in lowercase on the
171  * disk. Of course Windows doesn't care about casing. */
172  if (strtolower(buf, _searchpaths[sp].size() - 1) && FileExists(buf)) return buf;
173 #endif
174  }
175 
176  return {};
177 }
178 
179 std::string FioGetDirectory(Searchpath sp, Subdirectory subdir)
180 {
181  assert(subdir < NUM_SUBDIRS);
182  assert(sp < NUM_SEARCHPATHS);
183 
184  return _searchpaths[sp] + _subdirs[subdir];
185 }
186 
187 std::string FioFindDirectory(Subdirectory subdir)
188 {
189  /* Find and return the first valid directory */
190  for (Searchpath sp : _valid_searchpaths) {
191  std::string ret = FioGetDirectory(sp, subdir);
192  if (FileExists(ret)) return ret;
193  }
194 
195  /* Could not find the directory, fall back to a base path */
196  return _personal_dir;
197 }
198 
199 static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
200 {
201 #if defined(_WIN32)
202  /* fopen is implemented as a define with ellipses for
203  * Unicode support (prepend an L). As we are not sending
204  * a string, but a variable, it 'renames' the variable,
205  * so make that variable to makes it compile happily */
206  wchar_t Lmode[5];
207  MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
208 #endif
209  FILE *f = nullptr;
210  std::string buf;
211 
212  if (subdir == NO_DIRECTORY) {
213  buf = filename;
214  } else {
215  buf = _searchpaths[sp] + _subdirs[subdir] + filename;
216  }
217 
218 #if defined(_WIN32)
219  if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf).c_str()) == INVALID_FILE_ATTRIBUTES) return nullptr;
220 #endif
221 
222  f = fopen(buf.c_str(), mode);
223 #if !defined(_WIN32)
224  if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
225  f = fopen(buf.c_str(), mode);
226  }
227 #endif
228  if (f != nullptr && filesize != nullptr) {
229  /* Find the size of the file */
230  fseek(f, 0, SEEK_END);
231  *filesize = ftell(f);
232  fseek(f, 0, SEEK_SET);
233  }
234  return f;
235 }
236 
244 FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
245 {
246  FILE *f = fopen(entry.tar_filename.c_str(), "rb");
247  if (f == nullptr) return f;
248 
249  if (fseek(f, entry.position, SEEK_SET) < 0) {
250  fclose(f);
251  return nullptr;
252  }
253 
254  if (filesize != nullptr) *filesize = entry.size;
255  return f;
256 }
257 
264 FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
265 {
266  FILE *f = nullptr;
267 
268  assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
269 
270  for (Searchpath sp : _valid_searchpaths) {
271  f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
272  if (f != nullptr || subdir == NO_DIRECTORY) break;
273  }
274 
275  /* We can only use .tar in case of data-dir, and read-mode */
276  if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
277  /* Filenames in tars are always forced to be lowercase */
278  std::string resolved_name = filename;
279  strtolower(resolved_name);
280 
281  /* Resolve ".." */
282  std::istringstream ss(resolved_name);
283  std::vector<std::string> tokens;
284  std::string token;
285  while (std::getline(ss, token, PATHSEPCHAR)) {
286  if (token == "..") {
287  if (tokens.size() < 2) return nullptr;
288  tokens.pop_back();
289  } else if (token == ".") {
290  /* Do nothing. "." means current folder, but you can create tar files with "." in the path.
291  * This confuses our file resolver. So, act like this folder doesn't exist. */
292  } else {
293  tokens.push_back(token);
294  }
295  }
296 
297  resolved_name.clear();
298  bool first = true;
299  for (const std::string &token : tokens) {
300  if (!first) {
301  resolved_name += PATHSEP;
302  }
303  resolved_name += token;
304  first = false;
305  }
306 
307  /* Resolve ONE directory link */
308  for (const auto &link : _tar_linklist[subdir]) {
309  const std::string &src = link.first;
310  size_t len = src.length();
311  if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
312  /* Apply link */
313  resolved_name.replace(0, len, link.second);
314  break; // Only resolve one level
315  }
316  }
317 
318  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
319  if (it != _tar_filelist[subdir].end()) {
320  f = FioFOpenFileTar(it->second, filesize);
321  }
322  }
323 
324  /* Sometimes a full path is given. To support
325  * the 'subdirectory' must be 'removed'. */
326  if (f == nullptr && subdir != NO_DIRECTORY) {
327  switch (subdir) {
328  case BASESET_DIR:
329  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
330  if (f != nullptr) break;
331  [[fallthrough]];
332  case NEWGRF_DIR:
333  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
334  break;
335 
336  default:
337  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
338  break;
339  }
340  }
341 
342  return f;
343 }
344 
350 void FioCreateDirectory(const std::string &name)
351 {
352  auto p = name.find_last_of(PATHSEPCHAR);
353  if (p != std::string::npos) {
354  std::string dirname = name.substr(0, p);
355  DIR *dir = ttd_opendir(dirname.c_str());
356  if (dir == nullptr) {
357  FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
358  } else {
359  closedir(dir);
360  }
361  }
362 
363  /* Ignore directory creation errors; they'll surface later on, and most
364  * of the time they are 'directory already exists' errors anyhow. */
365 #if defined(_WIN32)
366  CreateDirectory(OTTD2FS(name).c_str(), nullptr);
367 #else
368  mkdir(OTTD2FS(name).c_str(), 0755);
369 #endif
370 }
371 
378 void AppendPathSeparator(std::string &buf)
379 {
380  if (buf.empty()) return;
381 
382  if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
383 }
384 
385 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
386 {
387  std::string src = srcParam;
388  std::string dest = destParam;
389  /* Tar internals assume lowercase */
390  std::transform(src.begin(), src.end(), src.begin(), tolower);
391  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
392 
393  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
394  if (dest_file != _tar_filelist[subdir].end()) {
395  /* Link to file. Process the link like the destination file. */
396  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
397  } else {
398  /* Destination file not found. Assume 'link to directory'
399  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
400  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
401  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
402  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
403  }
404 }
405 
411 static void SimplifyFileName(std::string &name)
412 {
413  for (char &c : name) {
414  /* Force lowercase */
415  c = std::tolower(c);
416 #if (PATHSEPCHAR != '/')
417  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
418  if (c == '/') c = PATHSEPCHAR;
419 #endif
420  }
421 }
422 
429 {
430  _tar_filelist[sd].clear();
431  _tar_list[sd].clear();
432  uint num = this->Scan(".tar", sd, false);
433  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
434  return num;
435 }
436 
437 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
438 {
439  Debug(misc, 2, "Scanning for tars");
440  TarScanner fs;
441  uint num = 0;
442  if (mode & TarScanner::BASESET) {
443  num += fs.DoScan(BASESET_DIR);
444  }
445  if (mode & TarScanner::NEWGRF) {
446  num += fs.DoScan(NEWGRF_DIR);
447  }
448  if (mode & TarScanner::AI) {
449  num += fs.DoScan(AI_DIR);
450  num += fs.DoScan(AI_LIBRARY_DIR);
451  }
452  if (mode & TarScanner::GAME) {
453  num += fs.DoScan(GAME_DIR);
454  num += fs.DoScan(GAME_LIBRARY_DIR);
455  }
456  if (mode & TarScanner::SCENARIO) {
457  num += fs.DoScan(SCENARIO_DIR);
458  num += fs.DoScan(HEIGHTMAP_DIR);
459  }
460  Debug(misc, 2, "Scan complete, found {} files", num);
461  return num;
462 }
463 
470 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
471 {
472  this->subdir = sd;
473  return this->AddFile(filename, 0);
474 }
475 
487 static std::string ExtractString(char *buffer, size_t buffer_length)
488 {
489  size_t length = 0;
490  for (; length < buffer_length && buffer[length] != '\0'; length++) {}
491  return StrMakeValid(std::string_view(buffer, length));
492 }
493 
494 bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] const std::string &tar_filename)
495 {
496  /* No tar within tar. */
497  assert(tar_filename.empty());
498 
499  /* The TAR-header, repeated for every file */
500  struct TarHeader {
501  char name[100];
502  char mode[8];
503  char uid[8];
504  char gid[8];
505  char size[12];
506  char mtime[12];
507  char chksum[8];
508  char typeflag;
509  char linkname[100];
510  char magic[6];
511  char version[2];
512  char uname[32];
513  char gname[32];
514  char devmajor[8];
515  char devminor[8];
516  char prefix[155];
517 
518  char unused[12];
519  };
520 
521  /* Check if we already seen this file */
522  TarList::iterator it = _tar_list[this->subdir].find(filename);
523  if (it != _tar_list[this->subdir].end()) return false;
524 
525  FILE *f = fopen(filename.c_str(), "rb");
526  /* Although the file has been found there can be
527  * a number of reasons we cannot open the file.
528  * Most common case is when we simply have not
529  * been given read access. */
530  if (f == nullptr) return false;
531 
532  _tar_list[this->subdir][filename] = std::string{};
533 
534  std::string filename_base = std::filesystem::path(filename).filename().string();
535  SimplifyFileName(filename_base);
536 
537  TarLinkList links;
538 
539  TarHeader th;
540  size_t num = 0, pos = 0;
541 
542  /* Make a char of 512 empty bytes */
543  char empty[512];
544  memset(&empty[0], 0, sizeof(empty));
545 
546  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
547  size_t num_bytes_read = fread(&th, 1, 512, f);
548  if (num_bytes_read != 512) break;
549  pos += num_bytes_read;
550 
551  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
552  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
553  /* If we have only zeros in the block, it can be an end-of-file indicator */
554  if (memcmp(&th, &empty[0], 512) == 0) continue;
555 
556  Debug(misc, 0, "The file '{}' isn't a valid tar-file", filename);
557  fclose(f);
558  return false;
559  }
560 
561  std::string name;
562 
563  /* The prefix contains the directory-name */
564  if (th.prefix[0] != '\0') {
565  name = ExtractString(th.prefix, lengthof(th.prefix));
566  name += PATHSEP;
567  }
568 
569  /* Copy the name of the file in a safe way at the end of 'name' */
570  name += ExtractString(th.name, lengthof(th.name));
571 
572  /* The size of the file, for some strange reason, this is stored as a string in octals. */
573  std::string size = ExtractString(th.size, lengthof(th.size));
574  size_t skip = 0;
575  if (!size.empty()) {
576  StrTrimInPlace(size);
577  auto [_, err] = std::from_chars(size.data(), size.data() + size.size(), skip, 8);
578  if (err != std::errc()) {
579  Debug(misc, 0, "The file '{}' has an invalid size for '{}'", filename, name);
580  fclose(f);
581  return false;
582  }
583  }
584 
585  switch (th.typeflag) {
586  case '\0':
587  case '0': { // regular file
588  if (name.empty()) break;
589 
590  /* Store this entry in the list */
591  TarFileListEntry entry;
592  entry.tar_filename = filename;
593  entry.size = skip;
594  entry.position = pos;
595 
596  /* Convert to lowercase and our PATHSEPCHAR */
597  SimplifyFileName(name);
598 
599  Debug(misc, 6, "Found file in tar: {} ({} bytes, {} offset)", name, skip, pos);
600  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(filename_base + PATHSEPCHAR + name, entry)).second) num++;
601 
602  break;
603  }
604 
605  case '1': // hard links
606  case '2': { // symbolic links
607  /* Copy the destination of the link in a safe way at the end of 'linkname' */
608  std::string link = ExtractString(th.linkname, lengthof(th.linkname));
609 
610  if (name.empty() || link.empty()) break;
611 
612  /* Convert to lowercase and our PATHSEPCHAR */
613  SimplifyFileName(name);
614  SimplifyFileName(link);
615 
616  /* Only allow relative links */
617  if (link[0] == PATHSEPCHAR) {
618  Debug(misc, 5, "Ignoring absolute link in tar: {} -> {}", name, link);
619  break;
620  }
621 
622  /* Process relative path.
623  * Note: The destination of links must not contain any directory-links. */
624  std::string dest = (std::filesystem::path(name).remove_filename() /= link).lexically_normal().string();
625  if (dest[0] == PATHSEPCHAR || dest.starts_with("..")) {
626  Debug(misc, 5, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
627  break;
628  }
629 
630  /* Store links in temporary list */
631  Debug(misc, 6, "Found link in tar: {} -> {}", name, dest);
632  links.insert(TarLinkList::value_type(filename_base + PATHSEPCHAR + name, filename_base + PATHSEPCHAR + dest));
633 
634  break;
635  }
636 
637  case '5': // directory
638  /* Convert to lowercase and our PATHSEPCHAR */
639  SimplifyFileName(name);
640 
641  /* Store the first directory name we detect */
642  Debug(misc, 6, "Found dir in tar: {}", name);
643  if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
644  break;
645 
646  default:
647  /* Ignore other types */
648  break;
649  }
650 
651  /* Skip to the next block.. */
652  skip = Align(skip, 512);
653  if (fseek(f, skip, SEEK_CUR) < 0) {
654  Debug(misc, 0, "The file '{}' can't be read as a valid tar-file", filename);
655  fclose(f);
656  return false;
657  }
658  pos += skip;
659  }
660 
661  Debug(misc, 4, "Found tar '{}' with {} new files", filename, num);
662  fclose(f);
663 
664  /* Resolve file links and store directory links.
665  * We restrict usage of links to two cases:
666  * 1) Links to directories:
667  * Both the source path and the destination path must NOT contain any further links.
668  * When resolving files at most one directory link is resolved.
669  * 2) Links to files:
670  * The destination path must NOT contain any links.
671  * The source path may contain one directory link.
672  */
673  for (auto &it : links) {
674  TarAddLink(it.first, it.second, this->subdir);
675  }
676 
677  return true;
678 }
679 
687 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
688 {
689  TarList::iterator it = _tar_list[subdir].find(tar_filename);
690  /* We don't know the file. */
691  if (it == _tar_list[subdir].end()) return false;
692 
693  const auto &dirname = (*it).second;
694 
695  /* The file doesn't have a sub directory! */
696  if (dirname.empty()) {
697  Debug(misc, 3, "Extracting {} failed; archive rejected, the contents must be in a sub directory", tar_filename);
698  return false;
699  }
700 
701  std::string filename = tar_filename;
702  auto p = filename.find_last_of(PATHSEPCHAR);
703  /* The file's path does not have a separator? */
704  if (p == std::string::npos) return false;
705 
706  filename.replace(p + 1, std::string::npos, dirname);
707  Debug(misc, 8, "Extracting {} to directory {}", tar_filename, filename);
708  FioCreateDirectory(filename);
709 
710  for (auto &it2 : _tar_filelist[subdir]) {
711  if (tar_filename != it2.second.tar_filename) continue;
712 
713  /* it2.first is tarball + PATHSEPCHAR + name. */
714  std::string_view name = it2.first;
715  name.remove_prefix(name.find_first_of(PATHSEPCHAR) + 1);
716  filename.replace(p + 1, std::string::npos, name);
717 
718  Debug(misc, 9, " extracting {}", filename);
719 
720  /* First open the file in the .tar. */
721  size_t to_copy = 0;
722  std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2.second, &to_copy));
723  if (!in) {
724  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
725  return false;
726  }
727 
728  /* Now open the 'output' file. */
729  std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
730  if (!out) {
731  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
732  return false;
733  }
734 
735  /* Now read from the tar and write it into the file. */
736  char buffer[4096];
737  size_t read;
738  for (; to_copy != 0; to_copy -= read) {
739  read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
740  if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
741  }
742 
743  if (to_copy != 0) {
744  Debug(misc, 6, "Extracting {} failed; still {} bytes to copy", filename, to_copy);
745  return false;
746  }
747  }
748 
749  Debug(misc, 9, " extraction successful");
750  return true;
751 }
752 
753 #if defined(_WIN32)
754 
759 extern void DetermineBasePaths(const char *exe);
760 #else /* defined(_WIN32) */
761 
769 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
770 {
771  std::string path = exe;
772 
773 #ifdef WITH_COCOA
774  for (size_t pos = path.find_first_of('.'); pos != std::string::npos; pos = path.find_first_of('.', pos + 1)) {
775  if (StrEqualsIgnoreCase(path.substr(pos, 4), ".app")) {
776  path.erase(pos);
777  break;
778  }
779  }
780 #endif /* WITH_COCOA */
781 
782  size_t pos = path.find_last_of(PATHSEPCHAR);
783  if (pos == std::string::npos) return false;
784 
785  path.erase(pos);
786 
787  if (chdir(path.c_str()) != 0) {
788  Debug(misc, 0, "Directory with the binary does not exist?");
789  return false;
790  }
791 
792  return true;
793 }
794 
806 {
807  /* No working directory, so nothing to do. */
808  if (_searchpaths[SP_WORKING_DIR].empty()) return false;
809 
810  /* Working directory is root, so do nothing. */
811  if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
812 
813  /* No personal/home directory, so the working directory won't be that. */
814  if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
815 
816  std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
817  AppendPathSeparator(tmp);
818 
819  return _searchpaths[SP_PERSONAL_DIR] != tmp;
820 }
821 
827 static std::string GetHomeDir()
828 {
829 #ifdef __HAIKU__
830  BPath path;
831  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
832  return std::string(path.Path());
833 #else
834  const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed
835  if (home_env != nullptr) return std::string(home_env);
836 
837  const struct passwd *pw = getpwuid(getuid());
838  if (pw != nullptr) return std::string(pw->pw_dir);
839 #endif
840  return {};
841 }
842 
847 void DetermineBasePaths(const char *exe)
848 {
849  std::string tmp;
850  const std::string homedir = GetHomeDir();
851 #ifdef USE_XDG
852  const char *xdg_data_home = std::getenv("XDG_DATA_HOME");
853  if (xdg_data_home != nullptr) {
854  tmp = xdg_data_home;
855  tmp += PATHSEP;
856  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
857  AppendPathSeparator(tmp);
858  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
859 
860  tmp += "content_download";
861  AppendPathSeparator(tmp);
863  } else if (!homedir.empty()) {
864  tmp = homedir;
865  tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
866  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
867  AppendPathSeparator(tmp);
868  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
869 
870  tmp += "content_download";
871  AppendPathSeparator(tmp);
873  } else {
874  _searchpaths[SP_PERSONAL_DIR_XDG].clear();
876  }
877 #endif
878 
879 #if !defined(WITH_PERSONAL_DIR)
880  _searchpaths[SP_PERSONAL_DIR].clear();
881 #else
882  if (!homedir.empty()) {
883  tmp = homedir;
884  tmp += PATHSEP;
885  tmp += PERSONAL_DIR;
886  AppendPathSeparator(tmp);
888 
889  tmp += "content_download";
890  AppendPathSeparator(tmp);
892  } else {
893  _searchpaths[SP_PERSONAL_DIR].clear();
895  }
896 #endif
897 
898 #if defined(WITH_SHARED_DIR)
899  tmp = SHARED_DIR;
900  AppendPathSeparator(tmp);
902 #else
903  _searchpaths[SP_SHARED_DIR].clear();
904 #endif
905 
906  char cwd[MAX_PATH];
907  if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
908 
909  if (_config_file.empty()) {
910  /* Get the path to working directory of OpenTTD. */
911  tmp = cwd;
912  AppendPathSeparator(tmp);
914 
916  } else {
917  /* Use the folder of the config file as working directory. */
918  size_t end = _config_file.find_last_of(PATHSEPCHAR);
919  if (end == std::string::npos) {
920  /* _config_file is not in a folder, so use current directory. */
921  tmp = cwd;
922  AppendPathSeparator(tmp);
924  } else {
925  _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
926  }
927  }
928 
929  /* Change the working directory to that one of the executable */
931  char buf[MAX_PATH];
932  if (getcwd(buf, lengthof(buf)) == nullptr) {
933  tmp.clear();
934  } else {
935  tmp = buf;
936  }
937  AppendPathSeparator(tmp);
939  } else {
940  _searchpaths[SP_BINARY_DIR].clear();
941  }
942 
943  if (cwd[0] != '\0') {
944  /* Go back to the current working directory. */
945  if (chdir(cwd) != 0) {
946  Debug(misc, 0, "Failed to return to working directory!");
947  }
948  }
949 
950 #if !defined(GLOBAL_DATA_DIR)
952 #else
953  tmp = GLOBAL_DATA_DIR;
954  AppendPathSeparator(tmp);
956 #endif
957 #ifdef WITH_COCOA
958 extern void CocoaSetApplicationBundleDir();
959  CocoaSetApplicationBundleDir();
960 #else
962 #endif
963 }
964 #endif /* defined(_WIN32) */
965 
966 std::string _personal_dir;
967 
975 void DeterminePaths(const char *exe, bool only_local_path)
976 {
977  DetermineBasePaths(exe);
978  FillValidSearchPaths(only_local_path);
979 
980 #ifdef USE_XDG
981  std::string config_home;
982  const std::string homedir = GetHomeDir();
983  const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME");
984  if (xdg_config_home != nullptr) {
985  config_home = xdg_config_home;
986  config_home += PATHSEP;
987  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
988  } else if (!homedir.empty()) {
989  /* Defaults to ~/.config */
990  config_home = homedir;
991  config_home += PATHSEP ".config" PATHSEP;
992  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
993  }
994  AppendPathSeparator(config_home);
995 #endif
996 
997  for (Searchpath sp : _valid_searchpaths) {
998  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
999  Debug(misc, 3, "{} added as search path", _searchpaths[sp]);
1000  }
1001 
1002  std::string config_dir;
1003  if (!_config_file.empty()) {
1004  config_dir = _searchpaths[SP_WORKING_DIR];
1005  } else {
1006  std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
1007  if (!personal_dir.empty()) {
1008  auto end = personal_dir.find_last_of(PATHSEPCHAR);
1009  if (end != std::string::npos) personal_dir.erase(end + 1);
1010  config_dir = personal_dir;
1011  } else {
1012 #ifdef USE_XDG
1013  /* No previous configuration file found. Use the configuration folder from XDG. */
1014  config_dir = config_home;
1015 #else
1016  static const Searchpath new_openttd_cfg_order[] = {
1018  };
1019 
1020  config_dir.clear();
1021  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1022  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1023  config_dir = _searchpaths[new_openttd_cfg_order[i]];
1024  break;
1025  }
1026  }
1027 #endif
1028  }
1029  _config_file = config_dir + "openttd.cfg";
1030  }
1031 
1032  Debug(misc, 1, "{} found as config directory", config_dir);
1033 
1034  _highscore_file = config_dir + "hs.dat";
1035  extern std::string _hotkeys_file;
1036  _hotkeys_file = config_dir + "hotkeys.cfg";
1037  extern std::string _windows_file;
1038  _windows_file = config_dir + "windows.cfg";
1039  extern std::string _private_file;
1040  _private_file = config_dir + "private.cfg";
1041  extern std::string _secrets_file;
1042  _secrets_file = config_dir + "secrets.cfg";
1043 
1044 #ifdef USE_XDG
1045  if (config_dir == config_home) {
1046  /* We are using the XDG configuration home for the config file,
1047  * then store the rest in the XDG data home folder. */
1048  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1049  if (only_local_path) {
1050  /* In case of XDG and we only want local paths and we detected that
1051  * the user either manually indicated the XDG path or didn't use
1052  * "-c" option, we change the working-dir to the XDG personal-dir,
1053  * as this is most likely what the user is expecting. */
1054  _searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
1055  }
1056  } else
1057 #endif
1058  {
1059  _personal_dir = config_dir;
1060  }
1061 
1062  /* Make the necessary folders */
1063  FioCreateDirectory(config_dir);
1064 #if defined(WITH_PERSONAL_DIR)
1066 #endif
1067 
1068  Debug(misc, 1, "{} found as personal directory", _personal_dir);
1069 
1070  static const Subdirectory default_subdirs[] = {
1072  };
1073 
1074  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1075  FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1076  }
1077 
1078  /* If we have network we make a directory for the autodownloading of content */
1079  _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1080  Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
1082  FillValidSearchPaths(only_local_path);
1083 
1084  /* Create the directory for each of the types of content */
1086  for (uint i = 0; i < lengthof(dirs); i++) {
1087  FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1088  }
1089 
1090  extern std::string _log_file;
1091  _log_file = _personal_dir + "openttd.log";
1092 }
1093 
1098 void SanitizeFilename(std::string &filename)
1099 {
1100  for (auto &c : filename) {
1101  switch (c) {
1102  /* The following characters are not allowed in filenames
1103  * on at least one of the supported operating systems: */
1104  case ':': case '\\': case '*': case '?': case '/':
1105  case '<': case '>': case '|': case '"':
1106  c = '_';
1107  break;
1108  }
1109  }
1110 }
1111 
1120 std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1121 {
1122  FILE *in = fopen(filename.c_str(), "rb");
1123  if (in == nullptr) return nullptr;
1124 
1125  FileCloser fc(in);
1126 
1127  fseek(in, 0, SEEK_END);
1128  size_t len = ftell(in);
1129  fseek(in, 0, SEEK_SET);
1130  if (len > maxsize) return nullptr;
1131 
1132  std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
1133 
1134  mem.get()[len] = 0;
1135  if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1136 
1137  lenp = len;
1138  return mem;
1139 }
1140 
1147 static bool MatchesExtension(const char *extension, const char *filename)
1148 {
1149  if (extension == nullptr) return true;
1150 
1151  const char *ext = strrchr(filename, extension[0]);
1152  return ext != nullptr && StrEqualsIgnoreCase(ext, extension);
1153 }
1154 
1164 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1165 {
1166  uint num = 0;
1167  struct stat sb;
1168  struct dirent *dirent;
1169  DIR *dir;
1170 
1171  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1172 
1173  while ((dirent = readdir(dir)) != nullptr) {
1174  std::string d_name = FS2OTTD(dirent->d_name);
1175 
1176  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1177 
1178  std::string filename(path);
1179  filename += d_name;
1180 
1181  if (S_ISDIR(sb.st_mode)) {
1182  /* Directory */
1183  if (!recursive) continue;
1184  if (d_name == "." || d_name == "..") continue;
1185  AppendPathSeparator(filename);
1186  num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1187  } else if (S_ISREG(sb.st_mode)) {
1188  /* File */
1189  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1190  }
1191  }
1192 
1193  closedir(dir);
1194 
1195  return num;
1196 }
1197 
1204 static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
1205 {
1206  uint num = 0;
1207  const auto &filename = tar.first;
1208 
1209  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
1210 
1211  return num;
1212 }
1213 
1223 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1224 {
1225  this->subdir = sd;
1226 
1227  uint num = 0;
1228 
1229  for (Searchpath sp : _valid_searchpaths) {
1230  /* Don't search in the working directory */
1231  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1232 
1233  std::string path = FioGetDirectory(sp, sd);
1234  num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1235  }
1236 
1237  if (tars && sd != NO_DIRECTORY) {
1238  for (const auto &tar : _tar_filelist[sd]) {
1239  num += ScanTar(this, extension, tar);
1240  }
1241  }
1242 
1243  switch (sd) {
1244  case BASESET_DIR:
1245  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1246  [[fallthrough]];
1247  case NEWGRF_DIR:
1248  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1249  break;
1250 
1251  default: break;
1252  }
1253 
1254  return num;
1255 }
1256 
1265 uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
1266 {
1267  std::string path(directory);
1268  AppendPathSeparator(path);
1269  return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1270 }
ScanTar
static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
Scan the given tar and add graphics sets when it finds one.
Definition: fileio.cpp:1204
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1147
SP_AUTODOWNLOAD_DIR
@ SP_AUTODOWNLOAD_DIR
Search within the autodownload directory.
Definition: fileio_type.h:143
DeterminePaths
void DeterminePaths(const char *exe, bool only_local_path)
Acquire the base paths (personal dir and game data dir), fill all other paths (save dir,...
Definition: fileio.cpp:975
ExtractTar
bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
Extract the tar with the given filename in the directory where the tar resides.
Definition: fileio.cpp:687
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition: fileio_type.h:110
_tar_linklist
static TarLinkList _tar_linklist[NUM_SUBDIRS]
List of directory links.
Definition: fileio.cpp:72
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:966
ttd_opendir
DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
Definition: fileio_func.h:111
SP_PERSONAL_DIR
@ SP_PERSONAL_DIR
Search in the personal directory.
Definition: fileio_type.h:138
FioFindFullPath
std::string FioFindFullPath(Subdirectory subdir, const std::string &filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:160
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:116
TarScanner::DoScan
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:428
GAME_LIBRARY_DIR
@ GAME_LIBRARY_DIR
Subdirectory for all GS libraries.
Definition: fileio_type.h:122
SCREENSHOT_DIR
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
Definition: fileio_type.h:123
SP_AUTODOWNLOAD_PERSONAL_DIR
@ SP_AUTODOWNLOAD_PERSONAL_DIR
Search within the autodownload directory located in the personal directory.
Definition: fileio_type.h:144
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:132
NUM_SUBDIRS
@ NUM_SUBDIRS
Number of subdirectories.
Definition: fileio_type.h:125
FileScanner::Scan
uint Scan(const char *extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Definition: fileio.cpp:1223
StrMakeValid
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition: string.cpp:114
HEIGHTMAP_DIR
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:113
spriteloader.hpp
SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
@ SP_AUTODOWNLOAD_PERSONAL_DIR_XDG
Search within the autodownload directory located in the personal directory (XDG variant)
Definition: fileio_type.h:145
fileio_func.h
SP_INSTALLATION_DIR
@ SP_INSTALLATION_DIR
Search in the installation directory.
Definition: fileio_type.h:141
AUTOSAVE_DIR
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition: fileio_type.h:111
_private_file
std::string _private_file
Private configuration file of OpenTTD.
Definition: settings.cpp:59
fios.h
FioFOpenFileTar
FILE * FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
Opens a file from inside a tar archive.
Definition: fileio.cpp:244
OLD_GM_DIR
@ OLD_GM_DIR
Old subdirectory for the music.
Definition: fileio_type.h:114
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:118
BASE_DIR
@ BASE_DIR
Base directory for all subdirectories.
Definition: fileio_type.h:109
_searchpaths
std::array< std::string, NUM_SEARCHPATHS > _searchpaths
The search paths OpenTTD could search through.
Definition: fileio.cpp:58
FS2OTTD
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition: win32.cpp:462
AI_DIR
@ AI_DIR
Subdirectory for all AI files.
Definition: fileio_type.h:119
FioFOpenFile
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:264
_do_scan_working_directory
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:35
StrTrimInPlace
void StrTrimInPlace(std::string &str)
Trim the spaces from given string in place, i.e.
Definition: string.cpp:288
_log_file
std::string _log_file
File to reroute output of a forked OpenTTD to.
Definition: dedicated.cpp:14
SimplifyFileName
static void SimplifyFileName(std::string &name)
Simplify filenames from tars.
Definition: fileio.cpp:411
tar_type.h
SanitizeFilename
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1098
FileExists
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:141
ChangeWorkingDirectoryToExecutable
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:769
FileScanner::AddFile
virtual bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)=0
Add a file with the given filename.
SP_APPLICATION_BUNDLE_DIR
@ SP_APPLICATION_BUNDLE_DIR
Search within the application bundle.
Definition: fileio_type.h:142
ReadFileToMem
std::unique_ptr< char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1120
safeguards.h
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:303
GAME_DIR
@ GAME_DIR
Subdirectory for all game scripts.
Definition: fileio_type.h:121
SCENARIO_DIR
@ SCENARIO_DIR
Base directory for all scenarios.
Definition: fileio_type.h:112
FileScanner::subdir
Subdirectory subdir
The current sub directory we are searching through.
Definition: fileio_func.h:39
TarScanner::NEWGRF
@ NEWGRF
Scan for non-base sets.
Definition: fileio_func.h:66
FioCreateDirectory
void FioCreateDirectory(const std::string &name)
Create a directory with the given name If the parent directory does not exist, it will try to create ...
Definition: fileio.cpp:350
SP_SHARED_DIR
@ SP_SHARED_DIR
Search in the shared directory, like 'Shared Files' under Windows.
Definition: fileio_type.h:139
stdafx.h
SP_WORKING_DIR
@ SP_WORKING_DIR
Search in the working directory.
Definition: fileio_type.h:134
NEWGRF_DIR
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition: fileio_type.h:117
AppendPathSeparator
void AppendPathSeparator(std::string &buf)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:378
TarScanner::GAME
@ GAME
Scan for game scripts.
Definition: fileio_func.h:69
TarScanner::AddFile
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename={}) override
Add a file with the given filename.
_secrets_file
std::string _secrets_file
Secrets configuration file of OpenTTD.
Definition: settings.cpp:60
string_func.h
_windows_file
std::string _windows_file
Config file to store WindowDesc.
Definition: window.cpp:102
SP_BINARY_DIR
@ SP_BINARY_DIR
Search in the directory where the binary resides.
Definition: fileio_type.h:140
TarScanner::SCENARIO
@ SCENARIO
Scan for scenarios and heightmaps.
Definition: fileio_func.h:68
_highscore_file
std::string _highscore_file
The file to store the highscore data in.
Definition: highscore.cpp:24
FioCheckFileExists
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:127
NO_DIRECTORY
@ NO_DIRECTORY
A path without any base directory.
Definition: fileio_type.h:126
TarScanner::AI
@ AI
Scan for AIs and its libraries.
Definition: fileio_func.h:67
GetHomeDir
static std::string GetHomeDir()
Gets the home directory of the user.
Definition: fileio.cpp:827
DIR
Definition: win32.cpp:65
ScanPath
static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
Scan a single directory (and recursively its children) and add any graphics sets that are found.
Definition: fileio.cpp:1164
StrEqualsIgnoreCase
bool StrEqualsIgnoreCase(const std::string_view str1, const std::string_view str2)
Compares two string( view)s for equality, while ignoring the case of the characters.
Definition: string.cpp:366
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:108
DetermineBasePaths
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
Definition: fileio.cpp:847
AI_LIBRARY_DIR
@ AI_LIBRARY_DIR
Subdirectory for all AI libraries.
Definition: fileio_type.h:120
TarScanner
Helper for scanning for files with tar as extension.
Definition: fileio_func.h:59
SOCIAL_INTEGRATION_DIR
@ SOCIAL_INTEGRATION_DIR
Subdirectory for all social integration plugins.
Definition: fileio_type.h:124
FileScanner
Helper for scanning for files with a given name.
Definition: fileio_func.h:37
TarScanner::BASESET
@ BASESET
Scan for base sets.
Definition: fileio_func.h:65
IsValidSearchPath
static bool IsValidSearchPath(Searchpath sp)
Checks whether the given search path is a valid search path.
Definition: fileio.cpp:81
ExtractString
static std::string ExtractString(char *buffer, size_t buffer_length)
Helper to extract a string for the tar header.
Definition: fileio.cpp:487
OLD_DATA_DIR
@ OLD_DATA_DIR
Old subdirectory for the data.
Definition: fileio_type.h:115
TarScanner::Mode
Mode
The mode of tar scanning.
Definition: fileio_func.h:63
Align
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:37
OTTD2FS
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
Definition: win32.cpp:479
DoScanWorkingDirectory
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:805
FioFCloseFile
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:149
debug.h
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:58
TarFileListEntry
Definition: tar_type.h:16