OpenTTD Source  14.0-beta3
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  filename.replace(p + 1, std::string::npos, it2.first);
704 
705  Debug(misc, 9, " extracting {}", filename);
706 
707  /* First open the file in the .tar. */
708  size_t to_copy = 0;
709  std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2.second, &to_copy));
710  if (!in) {
711  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
712  return false;
713  }
714 
715  /* Now open the 'output' file. */
716  std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
717  if (!out) {
718  Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
719  return false;
720  }
721 
722  /* Now read from the tar and write it into the file. */
723  char buffer[4096];
724  size_t read;
725  for (; to_copy != 0; to_copy -= read) {
726  read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
727  if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
728  }
729 
730  if (to_copy != 0) {
731  Debug(misc, 6, "Extracting {} failed; still {} bytes to copy", filename, to_copy);
732  return false;
733  }
734  }
735 
736  Debug(misc, 9, " extraction successful");
737  return true;
738 }
739 
740 #if defined(_WIN32)
741 
746 extern void DetermineBasePaths(const char *exe);
747 #else /* defined(_WIN32) */
748 
756 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
757 {
758  std::string path = exe;
759 
760 #ifdef WITH_COCOA
761  for (size_t pos = path.find_first_of('.'); pos != std::string::npos; pos = path.find_first_of('.', pos + 1)) {
762  if (StrEqualsIgnoreCase(path.substr(pos, 4), ".app")) {
763  path.erase(pos);
764  break;
765  }
766  }
767 #endif /* WITH_COCOA */
768 
769  size_t pos = path.find_last_of(PATHSEPCHAR);
770  if (pos == std::string::npos) return false;
771 
772  path.erase(pos);
773 
774  if (chdir(path.c_str()) != 0) {
775  Debug(misc, 0, "Directory with the binary does not exist?");
776  return false;
777  }
778 
779  return true;
780 }
781 
793 {
794  /* No working directory, so nothing to do. */
795  if (_searchpaths[SP_WORKING_DIR].empty()) return false;
796 
797  /* Working directory is root, so do nothing. */
798  if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
799 
800  /* No personal/home directory, so the working directory won't be that. */
801  if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
802 
803  std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
804  AppendPathSeparator(tmp);
805 
806  return _searchpaths[SP_PERSONAL_DIR] != tmp;
807 }
808 
814 static std::string GetHomeDir()
815 {
816 #ifdef __HAIKU__
817  BPath path;
818  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
819  return std::string(path.Path());
820 #else
821  const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed
822  if (home_env != nullptr) return std::string(home_env);
823 
824  const struct passwd *pw = getpwuid(getuid());
825  if (pw != nullptr) return std::string(pw->pw_dir);
826 #endif
827  return {};
828 }
829 
834 void DetermineBasePaths(const char *exe)
835 {
836  std::string tmp;
837  const std::string homedir = GetHomeDir();
838 #ifdef USE_XDG
839  const char *xdg_data_home = std::getenv("XDG_DATA_HOME");
840  if (xdg_data_home != nullptr) {
841  tmp = xdg_data_home;
842  tmp += PATHSEP;
843  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
844  AppendPathSeparator(tmp);
845  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
846 
847  tmp += "content_download";
848  AppendPathSeparator(tmp);
850  } else if (!homedir.empty()) {
851  tmp = homedir;
852  tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
853  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
854  AppendPathSeparator(tmp);
855  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
856 
857  tmp += "content_download";
858  AppendPathSeparator(tmp);
860  } else {
861  _searchpaths[SP_PERSONAL_DIR_XDG].clear();
863  }
864 #endif
865 
866 #if !defined(WITH_PERSONAL_DIR)
867  _searchpaths[SP_PERSONAL_DIR].clear();
868 #else
869  if (!homedir.empty()) {
870  tmp = homedir;
871  tmp += PATHSEP;
872  tmp += PERSONAL_DIR;
873  AppendPathSeparator(tmp);
875 
876  tmp += "content_download";
877  AppendPathSeparator(tmp);
879  } else {
880  _searchpaths[SP_PERSONAL_DIR].clear();
882  }
883 #endif
884 
885 #if defined(WITH_SHARED_DIR)
886  tmp = SHARED_DIR;
887  AppendPathSeparator(tmp);
889 #else
890  _searchpaths[SP_SHARED_DIR].clear();
891 #endif
892 
893  char cwd[MAX_PATH];
894  if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
895 
896  if (_config_file.empty()) {
897  /* Get the path to working directory of OpenTTD. */
898  tmp = cwd;
899  AppendPathSeparator(tmp);
901 
903  } else {
904  /* Use the folder of the config file as working directory. */
905  size_t end = _config_file.find_last_of(PATHSEPCHAR);
906  if (end == std::string::npos) {
907  /* _config_file is not in a folder, so use current directory. */
908  tmp = cwd;
909  AppendPathSeparator(tmp);
911  } else {
912  _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
913  }
914  }
915 
916  /* Change the working directory to that one of the executable */
918  char buf[MAX_PATH];
919  if (getcwd(buf, lengthof(buf)) == nullptr) {
920  tmp.clear();
921  } else {
922  tmp = buf;
923  }
924  AppendPathSeparator(tmp);
926  } else {
927  _searchpaths[SP_BINARY_DIR].clear();
928  }
929 
930  if (cwd[0] != '\0') {
931  /* Go back to the current working directory. */
932  if (chdir(cwd) != 0) {
933  Debug(misc, 0, "Failed to return to working directory!");
934  }
935  }
936 
937 #if !defined(GLOBAL_DATA_DIR)
939 #else
940  tmp = GLOBAL_DATA_DIR;
941  AppendPathSeparator(tmp);
943 #endif
944 #ifdef WITH_COCOA
945 extern void CocoaSetApplicationBundleDir();
946  CocoaSetApplicationBundleDir();
947 #else
949 #endif
950 }
951 #endif /* defined(_WIN32) */
952 
953 std::string _personal_dir;
954 
962 void DeterminePaths(const char *exe, bool only_local_path)
963 {
964  DetermineBasePaths(exe);
965  FillValidSearchPaths(only_local_path);
966 
967 #ifdef USE_XDG
968  std::string config_home;
969  const std::string homedir = GetHomeDir();
970  const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME");
971  if (xdg_config_home != nullptr) {
972  config_home = xdg_config_home;
973  config_home += PATHSEP;
974  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
975  } else if (!homedir.empty()) {
976  /* Defaults to ~/.config */
977  config_home = homedir;
978  config_home += PATHSEP ".config" PATHSEP;
979  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
980  }
981  AppendPathSeparator(config_home);
982 #endif
983 
984  for (Searchpath sp : _valid_searchpaths) {
985  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
986  Debug(misc, 3, "{} added as search path", _searchpaths[sp]);
987  }
988 
989  std::string config_dir;
990  if (!_config_file.empty()) {
991  config_dir = _searchpaths[SP_WORKING_DIR];
992  } else {
993  std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
994  if (!personal_dir.empty()) {
995  auto end = personal_dir.find_last_of(PATHSEPCHAR);
996  if (end != std::string::npos) personal_dir.erase(end + 1);
997  config_dir = personal_dir;
998  } else {
999 #ifdef USE_XDG
1000  /* No previous configuration file found. Use the configuration folder from XDG. */
1001  config_dir = config_home;
1002 #else
1003  static const Searchpath new_openttd_cfg_order[] = {
1005  };
1006 
1007  config_dir.clear();
1008  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1009  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1010  config_dir = _searchpaths[new_openttd_cfg_order[i]];
1011  break;
1012  }
1013  }
1014 #endif
1015  }
1016  _config_file = config_dir + "openttd.cfg";
1017  }
1018 
1019  Debug(misc, 1, "{} found as config directory", config_dir);
1020 
1021  _highscore_file = config_dir + "hs.dat";
1022  extern std::string _hotkeys_file;
1023  _hotkeys_file = config_dir + "hotkeys.cfg";
1024  extern std::string _windows_file;
1025  _windows_file = config_dir + "windows.cfg";
1026  extern std::string _private_file;
1027  _private_file = config_dir + "private.cfg";
1028  extern std::string _secrets_file;
1029  _secrets_file = config_dir + "secrets.cfg";
1030 
1031 #ifdef USE_XDG
1032  if (config_dir == config_home) {
1033  /* We are using the XDG configuration home for the config file,
1034  * then store the rest in the XDG data home folder. */
1035  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1036  if (only_local_path) {
1037  /* In case of XDG and we only want local paths and we detected that
1038  * the user either manually indicated the XDG path or didn't use
1039  * "-c" option, we change the working-dir to the XDG personal-dir,
1040  * as this is most likely what the user is expecting. */
1041  _searchpaths[SP_WORKING_DIR] = _searchpaths[SP_PERSONAL_DIR_XDG];
1042  }
1043  } else
1044 #endif
1045  {
1046  _personal_dir = config_dir;
1047  }
1048 
1049  /* Make the necessary folders */
1050  FioCreateDirectory(config_dir);
1051 #if defined(WITH_PERSONAL_DIR)
1053 #endif
1054 
1055  Debug(misc, 1, "{} found as personal directory", _personal_dir);
1056 
1057  static const Subdirectory default_subdirs[] = {
1059  };
1060 
1061  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1062  FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1063  }
1064 
1065  /* If we have network we make a directory for the autodownloading of content */
1066  _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1067  Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
1069  FillValidSearchPaths(only_local_path);
1070 
1071  /* Create the directory for each of the types of content */
1073  for (uint i = 0; i < lengthof(dirs); i++) {
1074  FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1075  }
1076 
1077  extern std::string _log_file;
1078  _log_file = _personal_dir + "openttd.log";
1079 }
1080 
1085 void SanitizeFilename(std::string &filename)
1086 {
1087  for (auto &c : filename) {
1088  switch (c) {
1089  /* The following characters are not allowed in filenames
1090  * on at least one of the supported operating systems: */
1091  case ':': case '\\': case '*': case '?': case '/':
1092  case '<': case '>': case '|': case '"':
1093  c = '_';
1094  break;
1095  }
1096  }
1097 }
1098 
1107 std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1108 {
1109  FILE *in = fopen(filename.c_str(), "rb");
1110  if (in == nullptr) return nullptr;
1111 
1112  FileCloser fc(in);
1113 
1114  fseek(in, 0, SEEK_END);
1115  size_t len = ftell(in);
1116  fseek(in, 0, SEEK_SET);
1117  if (len > maxsize) return nullptr;
1118 
1119  std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
1120 
1121  mem.get()[len] = 0;
1122  if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1123 
1124  lenp = len;
1125  return mem;
1126 }
1127 
1134 static bool MatchesExtension(const char *extension, const char *filename)
1135 {
1136  if (extension == nullptr) return true;
1137 
1138  const char *ext = strrchr(filename, extension[0]);
1139  return ext != nullptr && StrEqualsIgnoreCase(ext, extension);
1140 }
1141 
1151 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1152 {
1153  uint num = 0;
1154  struct stat sb;
1155  struct dirent *dirent;
1156  DIR *dir;
1157 
1158  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1159 
1160  while ((dirent = readdir(dir)) != nullptr) {
1161  std::string d_name = FS2OTTD(dirent->d_name);
1162 
1163  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1164 
1165  std::string filename(path);
1166  filename += d_name;
1167 
1168  if (S_ISDIR(sb.st_mode)) {
1169  /* Directory */
1170  if (!recursive) continue;
1171  if (d_name == "." || d_name == "..") continue;
1172  AppendPathSeparator(filename);
1173  num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1174  } else if (S_ISREG(sb.st_mode)) {
1175  /* File */
1176  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1177  }
1178  }
1179 
1180  closedir(dir);
1181 
1182  return num;
1183 }
1184 
1191 static uint ScanTar(FileScanner *fs, const char *extension, const TarFileList::value_type &tar)
1192 {
1193  uint num = 0;
1194  const auto &filename = tar.first;
1195 
1196  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, tar.second.tar_filename)) num++;
1197 
1198  return num;
1199 }
1200 
1210 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1211 {
1212  this->subdir = sd;
1213 
1214  uint num = 0;
1215 
1216  for (Searchpath sp : _valid_searchpaths) {
1217  /* Don't search in the working directory */
1218  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1219 
1220  std::string path = FioGetDirectory(sp, sd);
1221  num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1222  }
1223 
1224  if (tars && sd != NO_DIRECTORY) {
1225  for (const auto &tar : _tar_filelist[sd]) {
1226  num += ScanTar(this, extension, tar);
1227  }
1228  }
1229 
1230  switch (sd) {
1231  case BASESET_DIR:
1232  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1233  [[fallthrough]];
1234  case NEWGRF_DIR:
1235  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1236  break;
1237 
1238  default: break;
1239  }
1240 
1241  return num;
1242 }
1243 
1252 uint FileScanner::Scan(const char *extension, const std::string &directory, bool recursive)
1253 {
1254  std::string path(directory);
1255  AppendPathSeparator(path);
1256  return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1257 }
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:1191
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1134
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:962
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:953
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:1210
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:1085
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:756
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:1107
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:814
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:1151
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:834
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:792
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