OpenTTD Source  14.0-RC3
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 <sys/stat.h>
28 #include <sstream>
29 #include <filesystem>
30 
31 #include "safeguards.h"
32 
34 static bool _do_scan_working_directory = true;
35 
36 extern std::string _config_file;
37 extern std::string _highscore_file;
38 
39 static const char * const _subdirs[] = {
40  "",
41  "save" PATHSEP,
42  "save" PATHSEP "autosave" PATHSEP,
43  "scenario" PATHSEP,
44  "scenario" PATHSEP "heightmap" PATHSEP,
45  "gm" PATHSEP,
46  "data" PATHSEP,
47  "baseset" PATHSEP,
48  "newgrf" PATHSEP,
49  "lang" PATHSEP,
50  "ai" PATHSEP,
51  "ai" PATHSEP "library" PATHSEP,
52  "game" PATHSEP,
53  "game" PATHSEP "library" PATHSEP,
54  "screenshot" PATHSEP,
55  "social_integration" PATHSEP,
56 };
57 static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
58 
65 std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
66 std::vector<Searchpath> _valid_searchpaths;
67 std::array<TarList, NUM_SUBDIRS> _tar_list;
68 TarFileList _tar_filelist[NUM_SUBDIRS];
69 
70 typedef std::map<std::string, std::string> TarLinkList;
71 static TarLinkList _tar_linklist[NUM_SUBDIRS];
72 
73 extern bool FiosIsValidFile(const std::string &path, const struct dirent *ent, struct stat *sb);
74 
81 {
82  return sp < _searchpaths.size() && !_searchpaths[sp].empty();
83 }
84 
85 static void FillValidSearchPaths(bool only_local_path)
86 {
87  _valid_searchpaths.clear();
88 
89  std::set<std::string> seen{};
90  for (Searchpath sp = SP_FIRST_DIR; sp < NUM_SEARCHPATHS; sp++) {
91  if (sp == SP_WORKING_DIR) continue;
92 
93  if (only_local_path) {
94  switch (sp) {
95  case SP_WORKING_DIR: // Can be influence by "-c" option.
96  case SP_BINARY_DIR: // Most likely contains all the language files.
97  case SP_AUTODOWNLOAD_DIR: // Otherwise we cannot download in-game content.
98  break;
99 
100  default:
101  continue;
102  }
103  }
104 
105  if (IsValidSearchPath(sp)) {
106  if (seen.count(_searchpaths[sp]) != 0) continue;
107  seen.insert(_searchpaths[sp]);
108  _valid_searchpaths.emplace_back(sp);
109  }
110  }
111 
112  /* The working-directory is special, as it is controlled by _do_scan_working_directory.
113  * Only add the search path if it isn't already in the set. To preserve the same order
114  * as the enum, insert it in the front. */
115  if (IsValidSearchPath(SP_WORKING_DIR) && seen.count(_searchpaths[SP_WORKING_DIR]) == 0) {
116  _valid_searchpaths.insert(_valid_searchpaths.begin(), SP_WORKING_DIR);
117  }
118 }
119 
126 bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
127 {
128  FILE *f = FioFOpenFile(filename, "rb", subdir);
129  if (f == nullptr) return false;
130 
131  FioFCloseFile(f);
132  return true;
133 }
134 
140 bool FileExists(const std::string &filename)
141 {
142  return access(OTTD2FS(filename).c_str(), 0) == 0;
143 }
144 
148 void FioFCloseFile(FILE *f)
149 {
150  fclose(f);
151 }
152 
159 std::string FioFindFullPath(Subdirectory subdir, const std::string &filename)
160 {
161  assert(subdir < NUM_SUBDIRS);
162 
163  for (Searchpath sp : _valid_searchpaths) {
164  std::string buf = FioGetDirectory(sp, subdir);
165  buf += filename;
166  if (FileExists(buf)) return buf;
167 #if !defined(_WIN32)
168  /* Be, as opening files, aware that sometimes the filename
169  * might be in uppercase when it is in lowercase on the
170  * disk. Of course Windows doesn't care about casing. */
171  if (strtolower(buf, _searchpaths[sp].size() - 1) && FileExists(buf)) return buf;
172 #endif
173  }
174 
175  return {};
176 }
177 
178 std::string FioGetDirectory(Searchpath sp, Subdirectory subdir)
179 {
180  assert(subdir < NUM_SUBDIRS);
181  assert(sp < NUM_SEARCHPATHS);
182 
183  return _searchpaths[sp] + _subdirs[subdir];
184 }
185 
186 std::string FioFindDirectory(Subdirectory subdir)
187 {
188  /* Find and return the first valid directory */
189  for (Searchpath sp : _valid_searchpaths) {
190  std::string ret = FioGetDirectory(sp, subdir);
191  if (FileExists(ret)) return ret;
192  }
193 
194  /* Could not find the directory, fall back to a base path */
195  return _personal_dir;
196 }
197 
198 static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
199 {
200 #if defined(_WIN32)
201  /* fopen is implemented as a define with ellipses for
202  * Unicode support (prepend an L). As we are not sending
203  * a string, but a variable, it 'renames' the variable,
204  * so make that variable to makes it compile happily */
205  wchar_t Lmode[5];
206  MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
207 #endif
208  FILE *f = nullptr;
209  std::string buf;
210 
211  if (subdir == NO_DIRECTORY) {
212  buf = filename;
213  } else {
214  buf = _searchpaths[sp] + _subdirs[subdir] + filename;
215  }
216 
217 #if defined(_WIN32)
218  if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf).c_str()) == INVALID_FILE_ATTRIBUTES) return nullptr;
219 #endif
220 
221  f = fopen(buf.c_str(), mode);
222 #if !defined(_WIN32)
223  if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
224  f = fopen(buf.c_str(), mode);
225  }
226 #endif
227  if (f != nullptr && filesize != nullptr) {
228  /* Find the size of the file */
229  fseek(f, 0, SEEK_END);
230  *filesize = ftell(f);
231  fseek(f, 0, SEEK_SET);
232  }
233  return f;
234 }
235 
243 FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
244 {
245  FILE *f = fopen(entry.tar_filename.c_str(), "rb");
246  if (f == nullptr) return f;
247 
248  if (fseek(f, entry.position, SEEK_SET) < 0) {
249  fclose(f);
250  return nullptr;
251  }
252 
253  if (filesize != nullptr) *filesize = entry.size;
254  return f;
255 }
256 
263 FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
264 {
265  FILE *f = nullptr;
266 
267  assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
268 
269  for (Searchpath sp : _valid_searchpaths) {
270  f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
271  if (f != nullptr || subdir == NO_DIRECTORY) break;
272  }
273 
274  /* We can only use .tar in case of data-dir, and read-mode */
275  if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
276  /* Filenames in tars are always forced to be lowercase */
277  std::string resolved_name = filename;
278  strtolower(resolved_name);
279 
280  /* Resolve ".." */
281  std::istringstream ss(resolved_name);
282  std::vector<std::string> tokens;
283  std::string token;
284  while (std::getline(ss, token, PATHSEPCHAR)) {
285  if (token == "..") {
286  if (tokens.size() < 2) return nullptr;
287  tokens.pop_back();
288  } else if (token == ".") {
289  /* Do nothing. "." means current folder, but you can create tar files with "." in the path.
290  * This confuses our file resolver. So, act like this folder doesn't exist. */
291  } else {
292  tokens.push_back(token);
293  }
294  }
295 
296  resolved_name.clear();
297  bool first = true;
298  for (const std::string &token : tokens) {
299  if (!first) {
300  resolved_name += PATHSEP;
301  }
302  resolved_name += token;
303  first = false;
304  }
305 
306  /* Resolve ONE directory link */
307  for (const auto &link : _tar_linklist[subdir]) {
308  const std::string &src = link.first;
309  size_t len = src.length();
310  if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
311  /* Apply link */
312  resolved_name.replace(0, len, link.second);
313  break; // Only resolve one level
314  }
315  }
316 
317  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
318  if (it != _tar_filelist[subdir].end()) {
319  f = FioFOpenFileTar(it->second, filesize);
320  }
321  }
322 
323  /* Sometimes a full path is given. To support
324  * the 'subdirectory' must be 'removed'. */
325  if (f == nullptr && subdir != NO_DIRECTORY) {
326  switch (subdir) {
327  case BASESET_DIR:
328  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
329  if (f != nullptr) break;
330  [[fallthrough]];
331  case NEWGRF_DIR:
332  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
333  break;
334 
335  default:
336  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
337  break;
338  }
339  }
340 
341  return f;
342 }
343 
349 void FioCreateDirectory(const std::string &name)
350 {
351  auto p = name.find_last_of(PATHSEPCHAR);
352  if (p != std::string::npos) {
353  std::string dirname = name.substr(0, p);
354  DIR *dir = ttd_opendir(dirname.c_str());
355  if (dir == nullptr) {
356  FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
357  } else {
358  closedir(dir);
359  }
360  }
361 
362  /* Ignore directory creation errors; they'll surface later on, and most
363  * of the time they are 'directory already exists' errors anyhow. */
364 #if defined(_WIN32)
365  CreateDirectory(OTTD2FS(name).c_str(), nullptr);
366 #else
367  mkdir(OTTD2FS(name).c_str(), 0755);
368 #endif
369 }
370 
377 void AppendPathSeparator(std::string &buf)
378 {
379  if (buf.empty()) return;
380 
381  if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
382 }
383 
384 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
385 {
386  std::string src = srcParam;
387  std::string dest = destParam;
388  /* Tar internals assume lowercase */
389  std::transform(src.begin(), src.end(), src.begin(), tolower);
390  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
391 
392  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
393  if (dest_file != _tar_filelist[subdir].end()) {
394  /* Link to file. Process the link like the destination file. */
395  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
396  } else {
397  /* Destination file not found. Assume 'link to directory'
398  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
399  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
400  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
401  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
402  }
403 }
404 
410 static void SimplifyFileName(std::string &name)
411 {
412  for (char &c : name) {
413  /* Force lowercase */
414  c = std::tolower(c);
415 #if (PATHSEPCHAR != '/')
416  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
417  if (c == '/') c = PATHSEPCHAR;
418 #endif
419  }
420 }
421 
428 {
429  _tar_filelist[sd].clear();
430  _tar_list[sd].clear();
431  uint num = this->Scan(".tar", sd, false);
432  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
433  return num;
434 }
435 
436 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
437 {
438  Debug(misc, 2, "Scanning for tars");
439  TarScanner fs;
440  uint num = 0;
441  if (mode & TarScanner::BASESET) {
442  num += fs.DoScan(BASESET_DIR);
443  }
444  if (mode & TarScanner::NEWGRF) {
445  num += fs.DoScan(NEWGRF_DIR);
446  }
447  if (mode & TarScanner::AI) {
448  num += fs.DoScan(AI_DIR);
449  num += fs.DoScan(AI_LIBRARY_DIR);
450  }
451  if (mode & TarScanner::GAME) {
452  num += fs.DoScan(GAME_DIR);
453  num += fs.DoScan(GAME_LIBRARY_DIR);
454  }
455  if (mode & TarScanner::SCENARIO) {
456  num += fs.DoScan(SCENARIO_DIR);
457  num += fs.DoScan(HEIGHTMAP_DIR);
458  }
459  Debug(misc, 2, "Scan complete, found {} files", num);
460  return num;
461 }
462 
469 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
470 {
471  this->subdir = sd;
472  return this->AddFile(filename, 0);
473 }
474 
486 static std::string ExtractString(char *buffer, size_t buffer_length)
487 {
488  size_t length = 0;
489  for (; length < buffer_length && buffer[length] != '\0'; length++) {}
490  return StrMakeValid(std::string_view(buffer, length));
491 }
492 
493 bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] const std::string &tar_filename)
494 {
495  /* No tar within tar. */
496  assert(tar_filename.empty());
497 
498  /* The TAR-header, repeated for every file */
499  struct TarHeader {
500  char name[100];
501  char mode[8];
502  char uid[8];
503  char gid[8];
504  char size[12];
505  char mtime[12];
506  char chksum[8];
507  char typeflag;
508  char linkname[100];
509  char magic[6];
510  char version[2];
511  char uname[32];
512  char gname[32];
513  char devmajor[8];
514  char devminor[8];
515  char prefix[155];
516 
517  char unused[12];
518  };
519 
520  /* Check if we already seen this file */
521  TarList::iterator it = _tar_list[this->subdir].find(filename);
522  if (it != _tar_list[this->subdir].end()) return false;
523 
524  FILE *f = fopen(filename.c_str(), "rb");
525  /* Although the file has been found there can be
526  * a number of reasons we cannot open the file.
527  * Most common case is when we simply have not
528  * been given read access. */
529  if (f == nullptr) return false;
530 
531  _tar_list[this->subdir][filename] = std::string{};
532 
533  std::string filename_base = std::filesystem::path(filename).filename().string();
534  SimplifyFileName(filename_base);
535 
536  TarLinkList links;
537 
538  TarHeader th;
539  size_t num = 0, pos = 0;
540 
541  /* Make a char of 512 empty bytes */
542  char empty[512];
543  memset(&empty[0], 0, sizeof(empty));
544 
545  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
546  size_t num_bytes_read = fread(&th, 1, 512, f);
547  if (num_bytes_read != 512) break;
548  pos += num_bytes_read;
549 
550  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
551  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
552  /* If we have only zeros in the block, it can be an end-of-file indicator */
553  if (memcmp(&th, &empty[0], 512) == 0) continue;
554 
555  Debug(misc, 0, "The file '{}' isn't a valid tar-file", filename);
556  fclose(f);
557  return false;
558  }
559 
560  std::string name;
561 
562  /* The prefix contains the directory-name */
563  if (th.prefix[0] != '\0') {
564  name = ExtractString(th.prefix, lengthof(th.prefix));
565  name += PATHSEP;
566  }
567 
568  /* Copy the name of the file in a safe way at the end of 'name' */
569  name += ExtractString(th.name, lengthof(th.name));
570 
571  /* The size of the file, for some strange reason, this is stored as a string in octals. */
572  std::string size = ExtractString(th.size, lengthof(th.size));
573  size_t skip = size.empty() ? 0 : std::stoul(size, nullptr, 8);
574 
575  switch (th.typeflag) {
576  case '\0':
577  case '0': { // regular file
578  if (name.empty()) break;
579 
580  /* Store this entry in the list */
581  TarFileListEntry entry;
582  entry.tar_filename = filename;
583  entry.size = skip;
584  entry.position = pos;
585 
586  /* Convert to lowercase and our PATHSEPCHAR */
587  SimplifyFileName(name);
588 
589  Debug(misc, 6, "Found file in tar: {} ({} bytes, {} offset)", name, skip, pos);
590  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(filename_base + PATHSEPCHAR + name, entry)).second) num++;
591 
592  break;
593  }
594 
595  case '1': // hard links
596  case '2': { // symbolic links
597  /* Copy the destination of the link in a safe way at the end of 'linkname' */
598  std::string link = ExtractString(th.linkname, lengthof(th.linkname));
599 
600  if (name.empty() || link.empty()) break;
601 
602  /* Convert to lowercase and our PATHSEPCHAR */
603  SimplifyFileName(name);
604  SimplifyFileName(link);
605 
606  /* Only allow relative links */
607  if (link[0] == PATHSEPCHAR) {
608  Debug(misc, 5, "Ignoring absolute link in tar: {} -> {}", name, link);
609  break;
610  }
611 
612  /* Process relative path.
613  * Note: The destination of links must not contain any directory-links. */
614  std::string dest = (std::filesystem::path(name).remove_filename() /= link).lexically_normal().string();
615  if (dest[0] == PATHSEPCHAR || dest.starts_with("..")) {
616  Debug(misc, 5, "Ignoring link pointing outside of data directory: {} -> {}", name, link);
617  break;
618  }
619 
620  /* Store links in temporary list */
621  Debug(misc, 6, "Found link in tar: {} -> {}", name, dest);
622  links.insert(TarLinkList::value_type(filename_base + PATHSEPCHAR + name, filename_base + PATHSEPCHAR + dest));
623 
624  break;
625  }
626 
627  case '5': // directory
628  /* Convert to lowercase and our PATHSEPCHAR */
629  SimplifyFileName(name);
630 
631  /* Store the first directory name we detect */
632  Debug(misc, 6, "Found dir in tar: {}", name);
633  if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
634  break;
635 
636  default:
637  /* Ignore other types */
638  break;
639  }
640 
641  /* Skip to the next block.. */
642  skip = Align(skip, 512);
643  if (fseek(f, skip, SEEK_CUR) < 0) {
644  Debug(misc, 0, "The file '{}' can't be read as a valid tar-file", filename);
645  fclose(f);
646  return false;
647  }
648  pos += skip;
649  }
650 
651  Debug(misc, 4, "Found tar '{}' with {} new files", filename, num);
652  fclose(f);
653 
654  /* Resolve file links and store directory links.
655  * We restrict usage of links to two cases:
656  * 1) Links to directories:
657  * Both the source path and the destination path must NOT contain any further links.
658  * When resolving files at most one directory link is resolved.
659  * 2) Links to files:
660  * The destination path must NOT contain any links.
661  * The source path may contain one directory link.
662  */
663  for (auto &it : links) {
664  TarAddLink(it.first, it.second, this->subdir);
665  }
666 
667  return true;
668 }
669 
677 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
678 {
679  TarList::iterator it = _tar_list[subdir].find(tar_filename);
680  /* We don't know the file. */
681  if (it == _tar_list[subdir].end()) return false;
682 
683  const auto &dirname = (*it).second;
684 
685  /* The file doesn't have a sub directory! */
686  if (dirname.empty()) {
687  Debug(misc, 3, "Extracting {} failed; archive rejected, the contents must be in a sub directory", tar_filename);
688  return false;
689  }
690 
691  std::string filename = tar_filename;
692  auto p = filename.find_last_of(PATHSEPCHAR);
693  /* The file's path does not have a separator? */
694  if (p == std::string::npos) return false;
695 
696  filename.replace(p + 1, std::string::npos, dirname);
697  Debug(misc, 8, "Extracting {} to directory {}", tar_filename, filename);
698  FioCreateDirectory(filename);
699 
700  for (auto &it2 : _tar_filelist[subdir]) {
701  if (tar_filename != it2.second.tar_filename) continue;
702 
703  /* it2.first is tarball + PATHSEPCHAR + name. */
704  std::string_view name = it2.first;
705  name.remove_prefix(name.find_first_of(PATHSEPCHAR) + 1);
706  filename.replace(p + 1, std::string::npos, name);
707 
708  Debug(misc, 9, " extracting {}", filename);
709 
710  /* First open the file in the .tar. */
711  size_t to_copy = 0;
712  std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2.second, &to_copy));
713  if (!in) {
714  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
715  return false;
716  }
717 
718  /* Now open the 'output' file. */
719  std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
720  if (!out) {
721  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
722  return false;
723  }
724 
725  /* Now read from the tar and write it into the file. */
726  char buffer[4096];
727  size_t read;
728  for (; to_copy != 0; to_copy -= read) {
729  read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
730  if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
731  }
732 
733  if (to_copy != 0) {
734  Debug(misc, 6, "Extracting {} failed; still {} bytes to copy", filename, to_copy);
735  return false;
736  }
737  }
738 
739  Debug(misc, 9, " extraction successful");
740  return true;
741 }
742 
743 #if defined(_WIN32)
744 
749 extern void DetermineBasePaths(const char *exe);
750 #else /* defined(_WIN32) */
751 
759 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
760 {
761  std::string path = exe;
762 
763 #ifdef WITH_COCOA
764  for (size_t pos = path.find_first_of('.'); pos != std::string::npos; pos = path.find_first_of('.', pos + 1)) {
765  if (StrEqualsIgnoreCase(path.substr(pos, 4), ".app")) {
766  path.erase(pos);
767  break;
768  }
769  }
770 #endif /* WITH_COCOA */
771 
772  size_t pos = path.find_last_of(PATHSEPCHAR);
773  if (pos == std::string::npos) return false;
774 
775  path.erase(pos);
776 
777  if (chdir(path.c_str()) != 0) {
778  Debug(misc, 0, "Directory with the binary does not exist?");
779  return false;
780  }
781 
782  return true;
783 }
784 
796 {
797  /* No working directory, so nothing to do. */
798  if (_searchpaths[SP_WORKING_DIR].empty()) return false;
799 
800  /* Working directory is root, so do nothing. */
801  if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
802 
803  /* No personal/home directory, so the working directory won't be that. */
804  if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
805 
806  std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
807  AppendPathSeparator(tmp);
808 
809  return _searchpaths[SP_PERSONAL_DIR] != tmp;
810 }
811 
817 static std::string GetHomeDir()
818 {
819 #ifdef __HAIKU__
820  BPath path;
821  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
822  return std::string(path.Path());
823 #else
824  const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed
825  if (home_env != nullptr) return std::string(home_env);
826 
827  const struct passwd *pw = getpwuid(getuid());
828  if (pw != nullptr) return std::string(pw->pw_dir);
829 #endif
830  return {};
831 }
832 
837 void DetermineBasePaths(const char *exe)
838 {
839  std::string tmp;
840  const std::string homedir = GetHomeDir();
841 #ifdef USE_XDG
842  const char *xdg_data_home = std::getenv("XDG_DATA_HOME");
843  if (xdg_data_home != nullptr) {
844  tmp = xdg_data_home;
845  tmp += PATHSEP;
846  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
847  AppendPathSeparator(tmp);
848  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
849 
850  tmp += "content_download";
851  AppendPathSeparator(tmp);
853  } else if (!homedir.empty()) {
854  tmp = homedir;
855  tmp += PATHSEP ".local" PATHSEP "share" 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 {
864  _searchpaths[SP_PERSONAL_DIR_XDG].clear();
866  }
867 #endif
868 
869 #if !defined(WITH_PERSONAL_DIR)
870  _searchpaths[SP_PERSONAL_DIR].clear();
871 #else
872  if (!homedir.empty()) {
873  tmp = homedir;
874  tmp += PATHSEP;
875  tmp += PERSONAL_DIR;
876  AppendPathSeparator(tmp);
878 
879  tmp += "content_download";
880  AppendPathSeparator(tmp);
882  } else {
883  _searchpaths[SP_PERSONAL_DIR].clear();
885  }
886 #endif
887 
888 #if defined(WITH_SHARED_DIR)
889  tmp = SHARED_DIR;
890  AppendPathSeparator(tmp);
892 #else
893  _searchpaths[SP_SHARED_DIR].clear();
894 #endif
895 
896  char cwd[MAX_PATH];
897  if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
898 
899  if (_config_file.empty()) {
900  /* Get the path to working directory of OpenTTD. */
901  tmp = cwd;
902  AppendPathSeparator(tmp);
904 
906  } else {
907  /* Use the folder of the config file as working directory. */
908  size_t end = _config_file.find_last_of(PATHSEPCHAR);
909  if (end == std::string::npos) {
910  /* _config_file is not in a folder, so use current directory. */
911  tmp = cwd;
912  AppendPathSeparator(tmp);
914  } else {
915  _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
916  }
917  }
918 
919  /* Change the working directory to that one of the executable */
921  char buf[MAX_PATH];
922  if (getcwd(buf, lengthof(buf)) == nullptr) {
923  tmp.clear();
924  } else {
925  tmp = buf;
926  }
927  AppendPathSeparator(tmp);
929  } else {
930  _searchpaths[SP_BINARY_DIR].clear();
931  }
932 
933  if (cwd[0] != '\0') {
934  /* Go back to the current working directory. */
935  if (chdir(cwd) != 0) {
936  Debug(misc, 0, "Failed to return to working directory!");
937  }
938  }
939 
940 #if !defined(GLOBAL_DATA_DIR)
942 #else
943  tmp = GLOBAL_DATA_DIR;
944  AppendPathSeparator(tmp);
946 #endif
947 #ifdef WITH_COCOA
948 extern void CocoaSetApplicationBundleDir();
949  CocoaSetApplicationBundleDir();
950 #else
952 #endif
953 }
954 #endif /* defined(_WIN32) */
955 
956 std::string _personal_dir;
957 
965 void DeterminePaths(const char *exe, bool only_local_path)
966 {
967  DetermineBasePaths(exe);
968  FillValidSearchPaths(only_local_path);
969 
970 #ifdef USE_XDG
971  std::string config_home;
972  const std::string homedir = GetHomeDir();
973  const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME");
974  if (xdg_config_home != nullptr) {
975  config_home = xdg_config_home;
976  config_home += PATHSEP;
977  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
978  } else if (!homedir.empty()) {
979  /* Defaults to ~/.config */
980  config_home = homedir;
981  config_home += PATHSEP ".config" PATHSEP;
982  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
983  }
984  AppendPathSeparator(config_home);
985 #endif
986 
987  for (Searchpath sp : _valid_searchpaths) {
988  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
989  Debug(misc, 3, "{} added as search path", _searchpaths[sp]);
990  }
991 
992  std::string config_dir;
993  if (!_config_file.empty()) {
994  config_dir = _searchpaths[SP_WORKING_DIR];
995  } else {
996  std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
997  if (!personal_dir.empty()) {
998  auto end = personal_dir.find_last_of(PATHSEPCHAR);
999  if (end != std::string::npos) personal_dir.erase(end + 1);
1000  config_dir = personal_dir;
1001  } else {
1002 #ifdef USE_XDG
1003  /* No previous configuration file found. Use the configuration folder from XDG. */
1004  config_dir = config_home;
1005 #else
1006  static const Searchpath new_openttd_cfg_order[] = {
1008  };
1009 
1010  config_dir.clear();
1011  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1012  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1013  config_dir = _searchpaths[new_openttd_cfg_order[i]];
1014  break;
1015  }
1016  }
1017 #endif
1018  }
1019  _config_file = config_dir + "openttd.cfg";
1020  }
1021 
1022  Debug(misc, 1, "{} found as config directory", config_dir);
1023 
1024  _highscore_file = config_dir + "hs.dat";
1025  extern std::string _hotkeys_file;
1026  _hotkeys_file = config_dir + "hotkeys.cfg";
1027  extern std::string _windows_file;
1028  _windows_file = config_dir + "windows.cfg";
1029  extern std::string _private_file;
1030  _private_file = config_dir + "private.cfg";
1031  extern std::string _secrets_file;
1032  _secrets_file = config_dir + "secrets.cfg";
1033 
1034 #ifdef USE_XDG
1035  if (config_dir == config_home) {
1036  /* We are using the XDG configuration home for the config file,
1037  * then store the rest in the XDG data home folder. */
1038  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1039  if (only_local_path) {
1040  /* In case of XDG and we only want local paths and we detected that
1041  * the user either manually indicated the XDG path or didn't use
1042  * "-c" option, we change the working-dir to the XDG personal-dir,
1043  * as this is most likely what the user is expecting. */
1044  _searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
1045  }
1046  } else
1047 #endif
1048  {
1049  _personal_dir = config_dir;
1050  }
1051 
1052  /* Make the necessary folders */
1053  FioCreateDirectory(config_dir);
1054 #if defined(WITH_PERSONAL_DIR)
1056 #endif
1057 
1058  Debug(misc, 1, "{} found as personal directory", _personal_dir);
1059 
1060  static const Subdirectory default_subdirs[] = {
1062  };
1063 
1064  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1065  FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1066  }
1067 
1068  /* If we have network we make a directory for the autodownloading of content */
1069  _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1070  Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
1072  FillValidSearchPaths(only_local_path);
1073 
1074  /* Create the directory for each of the types of content */
1076  for (uint i = 0; i < lengthof(dirs); i++) {
1077  FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1078  }
1079 
1080  extern std::string _log_file;
1081  _log_file = _personal_dir + "openttd.log";
1082 }
1083 
1088 void SanitizeFilename(std::string &filename)
1089 {
1090  for (auto &c : filename) {
1091  switch (c) {
1092  /* The following characters are not allowed in filenames
1093  * on at least one of the supported operating systems: */
1094  case ':': case '\\': case '*': case '?': case '/':
1095  case '<': case '>': case '|': case '"':
1096  c = '_';
1097  break;
1098  }
1099  }
1100 }
1101 
1110 std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1111 {
1112  FILE *in = fopen(filename.c_str(), "rb");
1113  if (in == nullptr) return nullptr;
1114 
1115  FileCloser fc(in);
1116 
1117  fseek(in, 0, SEEK_END);
1118  size_t len = ftell(in);
1119  fseek(in, 0, SEEK_SET);
1120  if (len > maxsize) return nullptr;
1121 
1122  std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
1123 
1124  mem.get()[len] = 0;
1125  if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1126 
1127  lenp = len;
1128  return mem;
1129 }
1130 
1137 static bool MatchesExtension(const char *extension, const char *filename)
1138 {
1139  if (extension == nullptr) return true;
1140 
1141  const char *ext = strrchr(filename, extension[0]);
1142  return ext != nullptr && StrEqualsIgnoreCase(ext, extension);
1143 }
1144 
1154 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1155 {
1156  uint num = 0;
1157  struct stat sb;
1158  struct dirent *dirent;
1159  DIR *dir;
1160 
1161  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1162 
1163  while ((dirent = readdir(dir)) != nullptr) {
1164  std::string d_name = FS2OTTD(dirent->d_name);
1165 
1166  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1167 
1168  std::string filename(path);
1169  filename += d_name;
1170 
1171  if (S_ISDIR(sb.st_mode)) {
1172  /* Directory */
1173  if (!recursive) continue;
1174  if (d_name == "." || d_name == "..") continue;
1175  AppendPathSeparator(filename);
1176  num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1177  } else if (S_ISREG(sb.st_mode)) {
1178  /* File */
1179  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1180  }
1181  }
1182 
1183  closedir(dir);
1184 
1185  return num;
1186 }
1187 
1194 static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
1195 {
1196  uint num = 0;
1197  const auto &filename = tar.first;
1198 
1199  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
1200 
1201  return num;
1202 }
1203 
1213 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1214 {
1215  this->subdir = sd;
1216 
1217  uint num = 0;
1218 
1219  for (Searchpath sp : _valid_searchpaths) {
1220  /* Don't search in the working directory */
1221  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1222 
1223  std::string path = FioGetDirectory(sp, sd);
1224  num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1225  }
1226 
1227  if (tars && sd != NO_DIRECTORY) {
1228  for (const auto &tar : _tar_filelist[sd]) {
1229  num += ScanTar(this, extension, tar);
1230  }
1231  }
1232 
1233  switch (sd) {
1234  case BASESET_DIR:
1235  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1236  [[fallthrough]];
1237  case NEWGRF_DIR:
1238  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1239  break;
1240 
1241  default: break;
1242  }
1243 
1244  return num;
1245 }
1246 
1255 uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
1256 {
1257  std::string path(directory);
1258  AppendPathSeparator(path);
1259  return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1260 }
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:1194
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1137
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:965
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:677
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:71
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:956
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:159
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:427
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:1213
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:243
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:57
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:263
_do_scan_working_directory
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:34
_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:410
tar_type.h
SanitizeFilename
void SanitizeFilename(std::string &filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1088
FileExists
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:140
ChangeWorkingDirectoryToExecutable
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:759
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:1110
safeguards.h
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:349
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:377
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:126
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:817
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:1154
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:837
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
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:300
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:80
ExtractString
static std::string ExtractString(char *buffer, size_t buffer_length)
Helper to extract a string for the tar header.
Definition: fileio.cpp:486
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:795
FioFCloseFile
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:148
debug.h
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:58
TarFileListEntry
Definition: tar_type.h:16