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