OpenTTD Source  1.11.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"
12 #include "debug.h"
13 #include "fios.h"
14 #include "string_func.h"
15 #include "tar_type.h"
16 #ifdef _WIN32
17 #include <windows.h>
18 # define access _taccess
19 #elif defined(__HAIKU__)
20 #include <Path.h>
21 #include <storage/FindDirectory.h>
22 #else
23 #include <unistd.h>
24 #include <pwd.h>
25 #endif
26 #include <sys/stat.h>
27 #include <array>
28 #include <sstream>
29 
30 #include "safeguards.h"
31 
33 #define FIO_BUFFER_SIZE 512
34 
36 struct Fio {
37  byte *buffer, *buffer_end;
39  size_t pos;
40  FILE *cur_fh;
41  std::string filename;
42  std::array<FILE *, MAX_FILE_SLOTS> handles;
43  std::array<std::string, MAX_FILE_SLOTS> filenames;
44  std::array<std::string, MAX_FILE_SLOTS> shortnames;
45 };
46 
47 static Fio _fio;
48 
50 static bool _do_scan_working_directory = true;
51 
52 extern std::string _config_file;
53 extern std::string _highscore_file;
54 
59 size_t FioGetPos()
60 {
61  return _fio.pos + (_fio.buffer - _fio.buffer_end);
62 }
63 
69 const char *FioGetFilename(uint8 slot)
70 {
71  return _fio.shortnames[slot].c_str();
72 }
73 
79 void FioSeekTo(size_t pos, int mode)
80 {
81  if (mode == SEEK_CUR) pos += FioGetPos();
83  _fio.pos = pos;
84  if (fseek(_fio.cur_fh, _fio.pos, SEEK_SET) < 0) {
85  DEBUG(misc, 0, "Seeking in %s failed", _fio.filename.c_str());
86  }
87 }
88 
94 void FioSeekToFile(uint8 slot, size_t pos)
95 {
96  FILE *f = _fio.handles[slot];
97  assert(f != nullptr);
98  _fio.cur_fh = f;
99  _fio.filename = _fio.filenames[slot];
100  FioSeekTo(pos, SEEK_SET);
101 }
102 
108 {
109  if (_fio.buffer == _fio.buffer_end) {
110  _fio.buffer = _fio.buffer_start;
111  size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
112  _fio.pos += size;
113  _fio.buffer_end = _fio.buffer_start + size;
114 
115  if (size == 0) return 0;
116  }
117  return *_fio.buffer++;
118 }
119 
124 void FioSkipBytes(int n)
125 {
126  for (;;) {
127  int m = std::min<int>(_fio.buffer_end - _fio.buffer, n);
128  _fio.buffer += m;
129  n -= m;
130  if (n == 0) break;
131  FioReadByte();
132  n--;
133  }
134 }
135 
140 uint16 FioReadWord()
141 {
142  byte b = FioReadByte();
143  return (FioReadByte() << 8) | b;
144 }
145 
150 uint32 FioReadDword()
151 {
152  uint b = FioReadWord();
153  return (FioReadWord() << 16) | b;
154 }
155 
161 void FioReadBlock(void *ptr, size_t size)
162 {
163  FioSeekTo(FioGetPos(), SEEK_SET);
164  _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
165 }
166 
171 static inline void FioCloseFile(int slot)
172 {
173  if (_fio.handles[slot] != nullptr) {
174  fclose(_fio.handles[slot]);
175 
176  _fio.shortnames[slot].clear();
177 
178  _fio.handles[slot] = nullptr;
179  }
180 }
181 
184 {
185  for (int i = 0; i != lengthof(_fio.handles); i++) {
186  FioCloseFile(i);
187  }
188 }
189 
196 void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir)
197 {
198  FILE *f;
199 
200  f = FioFOpenFile(filename, "rb", subdir);
201  if (f == nullptr) usererror("Cannot open file '%s'", filename.c_str());
202  long pos = ftell(f);
203  if (pos < 0) usererror("Cannot read file '%s'", filename.c_str());
204 
205  FioCloseFile(slot); // if file was opened before, close it
206  _fio.handles[slot] = f;
207  _fio.filenames[slot] = filename;
208 
209  /* Store the filename without path and extension */
210  auto t = filename.rfind(PATHSEPCHAR);
211  std::string sn = filename.substr(t != std::string::npos ? t + 1 : 0);
212  _fio.shortnames[slot] = sn.substr(0, sn.rfind('.'));
213  strtolower(_fio.shortnames[slot]);
214 
215  FioSeekToFile(slot, (size_t)pos);
216 }
217 
218 static const char * const _subdirs[] = {
219  "",
220  "save" PATHSEP,
221  "save" PATHSEP "autosave" PATHSEP,
222  "scenario" PATHSEP,
223  "scenario" PATHSEP "heightmap" PATHSEP,
224  "gm" PATHSEP,
225  "data" PATHSEP,
226  "baseset" PATHSEP,
227  "newgrf" PATHSEP,
228  "lang" PATHSEP,
229  "ai" PATHSEP,
230  "ai" PATHSEP "library" PATHSEP,
231  "game" PATHSEP,
232  "game" PATHSEP "library" PATHSEP,
233  "screenshot" PATHSEP,
234 };
235 static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
236 
243 std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
244 std::array<TarList, NUM_SUBDIRS> _tar_list;
245 TarFileList _tar_filelist[NUM_SUBDIRS];
246 
247 typedef std::map<std::string, std::string> TarLinkList;
248 static TarLinkList _tar_linklist[NUM_SUBDIRS];
249 
256 {
257  return sp < _searchpaths.size() && !_searchpaths[sp].empty();
258 }
259 
266 bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
267 {
268  FILE *f = FioFOpenFile(filename, "rb", subdir);
269  if (f == nullptr) return false;
270 
271  FioFCloseFile(f);
272  return true;
273 }
274 
280 bool FileExists(const std::string &filename)
281 {
282  return access(OTTD2FS(filename.c_str()), 0) == 0;
283 }
284 
288 void FioFCloseFile(FILE *f)
289 {
290  fclose(f);
291 }
292 
299 std::string FioFindFullPath(Subdirectory subdir, const char *filename)
300 {
301  Searchpath sp;
302  assert(subdir < NUM_SUBDIRS);
303 
304  FOR_ALL_SEARCHPATHS(sp) {
305  std::string buf = FioGetDirectory(sp, subdir);
306  buf += filename;
307  if (FileExists(buf)) return buf;
308 #if !defined(_WIN32)
309  /* Be, as opening files, aware that sometimes the filename
310  * might be in uppercase when it is in lowercase on the
311  * disk. Of course Windows doesn't care about casing. */
312  if (strtolower(buf, _searchpaths[sp].size() - 1) && FileExists(buf)) return buf;
313 #endif
314  }
315 
316  return {};
317 }
318 
319 std::string FioGetDirectory(Searchpath sp, Subdirectory subdir)
320 {
321  assert(subdir < NUM_SUBDIRS);
322  assert(sp < NUM_SEARCHPATHS);
323 
324  return _searchpaths[sp] + _subdirs[subdir];
325 }
326 
327 std::string FioFindDirectory(Subdirectory subdir)
328 {
329  Searchpath sp;
330 
331  /* Find and return the first valid directory */
332  FOR_ALL_SEARCHPATHS(sp) {
333  std::string ret = FioGetDirectory(sp, subdir);
334  if (FileExists(ret)) return ret;
335  }
336 
337  /* Could not find the directory, fall back to a base path */
338  return _personal_dir;
339 }
340 
341 static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
342 {
343 #if defined(_WIN32)
344  /* fopen is implemented as a define with ellipses for
345  * Unicode support (prepend an L). As we are not sending
346  * a string, but a variable, it 'renames' the variable,
347  * so make that variable to makes it compile happily */
348  wchar_t Lmode[5];
349  MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
350 #endif
351  FILE *f = nullptr;
352  std::string buf;
353 
354  if (subdir == NO_DIRECTORY) {
355  buf = filename;
356  } else {
357  buf = _searchpaths[sp] + _subdirs[subdir] + filename;
358  }
359 
360 #if defined(_WIN32)
361  if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf.c_str())) == INVALID_FILE_ATTRIBUTES) return nullptr;
362 #endif
363 
364  f = fopen(buf.c_str(), mode);
365 #if !defined(_WIN32)
366  if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
367  f = fopen(buf.c_str(), mode);
368  }
369 #endif
370  if (f != nullptr && filesize != nullptr) {
371  /* Find the size of the file */
372  fseek(f, 0, SEEK_END);
373  *filesize = ftell(f);
374  fseek(f, 0, SEEK_SET);
375  }
376  return f;
377 }
378 
386 FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
387 {
388  FILE *f = fopen(entry.tar_filename.c_str(), "rb");
389  if (f == nullptr) return f;
390 
391  if (fseek(f, entry.position, SEEK_SET) < 0) {
392  fclose(f);
393  return nullptr;
394  }
395 
396  if (filesize != nullptr) *filesize = entry.size;
397  return f;
398 }
399 
406 FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
407 {
408  FILE *f = nullptr;
409  Searchpath sp;
410 
411  assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
412 
413  FOR_ALL_SEARCHPATHS(sp) {
414  f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
415  if (f != nullptr || subdir == NO_DIRECTORY) break;
416  }
417 
418  /* We can only use .tar in case of data-dir, and read-mode */
419  if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
420  /* Filenames in tars are always forced to be lowercase */
421  std::string resolved_name = filename;
422  strtolower(resolved_name);
423 
424  /* Resolve ".." */
425  std::istringstream ss(resolved_name);
426  std::vector<std::string> tokens;
427  std::string token;
428  while (std::getline(ss, token, PATHSEPCHAR)) {
429  if (token == "..") {
430  if (tokens.size() < 2) return nullptr;
431  tokens.pop_back();
432  } else {
433  tokens.push_back(token);
434  }
435  }
436 
437  resolved_name.clear();
438  bool first = true;
439  for (const std::string &token : tokens) {
440  if (!first) {
441  resolved_name += PATHSEP;
442  }
443  resolved_name += token;
444  first = false;
445  }
446 
447  /* Resolve ONE directory link */
448  for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
449  const std::string &src = link->first;
450  size_t len = src.length();
451  if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
452  /* Apply link */
453  resolved_name.replace(0, len, link->second);
454  break; // Only resolve one level
455  }
456  }
457 
458  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
459  if (it != _tar_filelist[subdir].end()) {
460  f = FioFOpenFileTar(it->second, filesize);
461  }
462  }
463 
464  /* Sometimes a full path is given. To support
465  * the 'subdirectory' must be 'removed'. */
466  if (f == nullptr && subdir != NO_DIRECTORY) {
467  switch (subdir) {
468  case BASESET_DIR:
469  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
470  if (f != nullptr) break;
471  FALLTHROUGH;
472  case NEWGRF_DIR:
473  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
474  break;
475 
476  default:
477  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
478  break;
479  }
480  }
481 
482  return f;
483 }
484 
490 void FioCreateDirectory(const std::string &name)
491 {
492  auto p = name.find_last_of(PATHSEPCHAR);
493  if (p != std::string::npos) {
494  std::string dirname = name.substr(0, p);
495  DIR *dir = ttd_opendir(dirname.c_str());
496  if (dir == nullptr) {
497  FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
498  } else {
499  closedir(dir);
500  }
501  }
502 
503  /* Ignore directory creation errors; they'll surface later on, and most
504  * of the time they are 'directory already exists' errors anyhow. */
505 #if defined(_WIN32)
506  CreateDirectory(OTTD2FS(name.c_str()), nullptr);
507 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
508  mkdir(OTTD2FS(name.c_str()));
509 #else
510  mkdir(OTTD2FS(name.c_str()), 0755);
511 #endif
512 }
513 
520 void AppendPathSeparator(std::string &buf)
521 {
522  if (buf.empty()) return;
523 
524  if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
525 }
526 
527 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
528 {
529  std::string src = srcParam;
530  std::string dest = destParam;
531  /* Tar internals assume lowercase */
532  std::transform(src.begin(), src.end(), src.begin(), tolower);
533  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
534 
535  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
536  if (dest_file != _tar_filelist[subdir].end()) {
537  /* Link to file. Process the link like the destination file. */
538  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
539  } else {
540  /* Destination file not found. Assume 'link to directory'
541  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
542  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
543  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
544  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
545  }
546 }
547 
553 static void SimplifyFileName(char *name)
554 {
555  /* Force lowercase */
556  strtolower(name);
557 
558  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
559 #if (PATHSEPCHAR != '/')
560  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
561 #endif
562 }
563 
570 {
571  _tar_filelist[sd].clear();
572  _tar_list[sd].clear();
573  uint num = this->Scan(".tar", sd, false);
574  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
575  return num;
576 }
577 
578 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
579 {
580  DEBUG(misc, 1, "Scanning for tars");
581  TarScanner fs;
582  uint num = 0;
583  if (mode & TarScanner::BASESET) {
584  num += fs.DoScan(BASESET_DIR);
585  }
586  if (mode & TarScanner::NEWGRF) {
587  num += fs.DoScan(NEWGRF_DIR);
588  }
589  if (mode & TarScanner::AI) {
590  num += fs.DoScan(AI_DIR);
591  num += fs.DoScan(AI_LIBRARY_DIR);
592  }
593  if (mode & TarScanner::GAME) {
594  num += fs.DoScan(GAME_DIR);
595  num += fs.DoScan(GAME_LIBRARY_DIR);
596  }
597  if (mode & TarScanner::SCENARIO) {
598  num += fs.DoScan(SCENARIO_DIR);
599  num += fs.DoScan(HEIGHTMAP_DIR);
600  }
601  DEBUG(misc, 1, "Scan complete, found %d files", num);
602  return num;
603 }
604 
611 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
612 {
613  this->subdir = sd;
614  return this->AddFile(filename, 0);
615 }
616 
617 bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)
618 {
619  /* No tar within tar. */
620  assert(tar_filename.empty());
621 
622  /* The TAR-header, repeated for every file */
623  struct TarHeader {
624  char name[100];
625  char mode[8];
626  char uid[8];
627  char gid[8];
628  char size[12];
629  char mtime[12];
630  char chksum[8];
631  char typeflag;
632  char linkname[100];
633  char magic[6];
634  char version[2];
635  char uname[32];
636  char gname[32];
637  char devmajor[8];
638  char devminor[8];
639  char prefix[155];
640 
641  char unused[12];
642  };
643 
644  /* Check if we already seen this file */
645  TarList::iterator it = _tar_list[this->subdir].find(filename);
646  if (it != _tar_list[this->subdir].end()) return false;
647 
648  FILE *f = fopen(filename.c_str(), "rb");
649  /* Although the file has been found there can be
650  * a number of reasons we cannot open the file.
651  * Most common case is when we simply have not
652  * been given read access. */
653  if (f == nullptr) return false;
654 
655  _tar_list[this->subdir][filename] = std::string{};
656 
657  TarLinkList links;
658 
659  TarHeader th;
660  char buf[sizeof(th.name) + 1], *end;
661  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
662  char link[sizeof(th.linkname) + 1];
663  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
664  size_t num = 0, pos = 0;
665 
666  /* Make a char of 512 empty bytes */
667  char empty[512];
668  memset(&empty[0], 0, sizeof(empty));
669 
670  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
671  size_t num_bytes_read = fread(&th, 1, 512, f);
672  if (num_bytes_read != 512) break;
673  pos += num_bytes_read;
674 
675  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
676  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
677  /* If we have only zeros in the block, it can be an end-of-file indicator */
678  if (memcmp(&th, &empty[0], 512) == 0) continue;
679 
680  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename.c_str());
681  fclose(f);
682  return false;
683  }
684 
685  name[0] = '\0';
686 
687  /* The prefix contains the directory-name */
688  if (th.prefix[0] != '\0') {
689  strecpy(name, th.prefix, lastof(name));
690  strecat(name, PATHSEP, lastof(name));
691  }
692 
693  /* Copy the name of the file in a safe way at the end of 'name' */
694  strecat(name, th.name, lastof(name));
695 
696  /* Calculate the size of the file.. for some strange reason this is stored as a string */
697  strecpy(buf, th.size, lastof(buf));
698  size_t skip = strtoul(buf, &end, 8);
699 
700  switch (th.typeflag) {
701  case '\0':
702  case '0': { // regular file
703  /* Ignore empty files */
704  if (skip == 0) break;
705 
706  if (strlen(name) == 0) break;
707 
708  /* Store this entry in the list */
709  TarFileListEntry entry;
710  entry.tar_filename = filename;
711  entry.size = skip;
712  entry.position = pos;
713 
714  /* Convert to lowercase and our PATHSEPCHAR */
715  SimplifyFileName(name);
716 
717  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
718  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
719 
720  break;
721  }
722 
723  case '1': // hard links
724  case '2': { // symbolic links
725  /* Copy the destination of the link in a safe way at the end of 'linkname' */
726  strecpy(link, th.linkname, lastof(link));
727 
728  if (strlen(name) == 0 || strlen(link) == 0) break;
729 
730  /* Convert to lowercase and our PATHSEPCHAR */
731  SimplifyFileName(name);
732  SimplifyFileName(link);
733 
734  /* Only allow relative links */
735  if (link[0] == PATHSEPCHAR) {
736  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
737  break;
738  }
739 
740  /* Process relative path.
741  * Note: The destination of links must not contain any directory-links. */
742  strecpy(dest, name, lastof(dest));
743  char *destpos = strrchr(dest, PATHSEPCHAR);
744  if (destpos == nullptr) destpos = dest;
745  *destpos = '\0';
746 
747  char *pos = link;
748  while (*pos != '\0') {
749  char *next = strchr(pos, PATHSEPCHAR);
750  if (next == nullptr) {
751  next = pos + strlen(pos);
752  } else {
753  /* Terminate the substring up to the path separator character. */
754  *next++= '\0';
755  }
756 
757  if (strcmp(pos, ".") == 0) {
758  /* Skip '.' (current dir) */
759  } else if (strcmp(pos, "..") == 0) {
760  /* level up */
761  if (dest[0] == '\0') {
762  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
763  break;
764  }
765 
766  /* Truncate 'dest' after last PATHSEPCHAR.
767  * This assumes that the truncated part is a real directory and not a link. */
768  destpos = strrchr(dest, PATHSEPCHAR);
769  if (destpos == nullptr) destpos = dest;
770  *destpos = '\0';
771  } else {
772  /* Append at end of 'dest' */
773  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
774  destpos = strecpy(destpos, pos, lastof(dest));
775  }
776 
777  if (destpos >= lastof(dest)) {
778  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename.c_str());
779  fclose(f);
780  return false;
781  }
782 
783  pos = next;
784  }
785 
786  /* Store links in temporary list */
787  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
788  links.insert(TarLinkList::value_type(name, dest));
789 
790  break;
791  }
792 
793  case '5': // directory
794  /* Convert to lowercase and our PATHSEPCHAR */
795  SimplifyFileName(name);
796 
797  /* Store the first directory name we detect */
798  DEBUG(misc, 6, "Found dir in tar: %s", name);
799  if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
800  break;
801 
802  default:
803  /* Ignore other types */
804  break;
805  }
806 
807  /* Skip to the next block.. */
808  skip = Align(skip, 512);
809  if (fseek(f, skip, SEEK_CUR) < 0) {
810  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename.c_str());
811  fclose(f);
812  return false;
813  }
814  pos += skip;
815  }
816 
817  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename.c_str(), num);
818  fclose(f);
819 
820  /* Resolve file links and store directory links.
821  * We restrict usage of links to two cases:
822  * 1) Links to directories:
823  * Both the source path and the destination path must NOT contain any further links.
824  * When resolving files at most one directory link is resolved.
825  * 2) Links to files:
826  * The destination path must NOT contain any links.
827  * The source path may contain one directory link.
828  */
829  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
830  const std::string &src = link->first;
831  const std::string &dest = link->second;
832  TarAddLink(src, dest, this->subdir);
833  }
834 
835  return true;
836 }
837 
845 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
846 {
847  TarList::iterator it = _tar_list[subdir].find(tar_filename);
848  /* We don't know the file. */
849  if (it == _tar_list[subdir].end()) return false;
850 
851  const auto &dirname = (*it).second;
852 
853  /* The file doesn't have a sub directory! */
854  if (dirname.empty()) {
855  DEBUG(misc, 1, "Extracting %s failed; archive rejected, the contents must be in a sub directory", tar_filename.c_str());
856  return false;
857  }
858 
859  std::string filename = tar_filename;
860  auto p = filename.find_last_of(PATHSEPCHAR);
861  /* The file's path does not have a separator? */
862  if (p == std::string::npos) return false;
863 
864  filename.replace(p + 1, std::string::npos, dirname);
865  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename.c_str(), filename.c_str());
866  FioCreateDirectory(filename);
867 
868  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
869  if (tar_filename != it2->second.tar_filename) continue;
870 
871  filename.replace(p + 1, std::string::npos, it2->first);
872 
873  DEBUG(misc, 9, " extracting %s", filename.c_str());
874 
875  /* First open the file in the .tar. */
876  size_t to_copy = 0;
877  std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2->second, &to_copy));
878  if (!in) {
879  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), tar_filename.c_str());
880  return false;
881  }
882 
883  /* Now open the 'output' file. */
884  std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
885  if (!out) {
886  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), filename.c_str());
887  return false;
888  }
889 
890  /* Now read from the tar and write it into the file. */
891  char buffer[4096];
892  size_t read;
893  for (; to_copy != 0; to_copy -= read) {
894  read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
895  if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
896  }
897 
898  if (to_copy != 0) {
899  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename.c_str(), (int)to_copy);
900  return false;
901  }
902  }
903 
904  DEBUG(misc, 9, " extraction successful");
905  return true;
906 }
907 
908 #if defined(_WIN32)
909 
914 extern void DetermineBasePaths(const char *exe);
915 #else /* defined(_WIN32) */
916 
924 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
925 {
926  char tmp[MAX_PATH];
927  strecpy(tmp, exe, lastof(tmp));
928 
929  bool success = false;
930 #ifdef WITH_COCOA
931  char *app_bundle = strchr(tmp, '.');
932  while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
933 
934  if (app_bundle != nullptr) *app_bundle = '\0';
935 #endif /* WITH_COCOA */
936  char *s = strrchr(tmp, PATHSEPCHAR);
937  if (s != nullptr) {
938  *s = '\0';
939  if (chdir(tmp) != 0) {
940  DEBUG(misc, 0, "Directory with the binary does not exist?");
941  } else {
942  success = true;
943  }
944  }
945  return success;
946 }
947 
959 {
960  /* No working directory, so nothing to do. */
961  if (_searchpaths[SP_WORKING_DIR].empty()) return false;
962 
963  /* Working directory is root, so do nothing. */
964  if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
965 
966  /* No personal/home directory, so the working directory won't be that. */
967  if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
968 
969  std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
970  AppendPathSeparator(tmp);
971 
972  return _searchpaths[SP_PERSONAL_DIR] != tmp;
973 }
974 
980 static std::string GetHomeDir()
981 {
982 #ifdef __HAIKU__
983  BPath path;
984  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
985  return std::string(path.Path());
986 #else
987  const char *home_env = getenv("HOME"); // Stack var, shouldn't be freed
988  if (home_env != nullptr) return std::string(home_env);
989 
990  const struct passwd *pw = getpwuid(getuid());
991  if (pw != nullptr) return std::string(pw->pw_dir);
992 #endif
993  return {};
994 }
995 
1000 void DetermineBasePaths(const char *exe)
1001 {
1002  std::string tmp;
1003  const std::string homedir = GetHomeDir();
1004 #ifdef USE_XDG
1005  const char *xdg_data_home = getenv("XDG_DATA_HOME");
1006  if (xdg_data_home != nullptr) {
1007  tmp = xdg_data_home;
1008  tmp += PATHSEP;
1009  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1010  AppendPathSeparator(tmp);
1011  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1012 
1013  tmp += "content_download";
1014  AppendPathSeparator(tmp);
1016  } else if (!homedir.empty()) {
1017  tmp = homedir;
1018  tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
1019  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1020  AppendPathSeparator(tmp);
1021  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1022 
1023  tmp += "content_download";
1024  AppendPathSeparator(tmp);
1026  } else {
1027  _searchpaths[SP_PERSONAL_DIR_XDG].clear();
1029  }
1030 #endif
1031 
1032 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1033  _searchpaths[SP_PERSONAL_DIR].clear();
1034 #else
1035  if (!homedir.empty()) {
1036  tmp = homedir;
1037  tmp += PATHSEP;
1038  tmp += PERSONAL_DIR;
1039  AppendPathSeparator(tmp);
1041 
1042  tmp += "content_download";
1043  AppendPathSeparator(tmp);
1045  } else {
1046  _searchpaths[SP_PERSONAL_DIR].clear();
1048  }
1049 #endif
1050 
1051 #if defined(WITH_SHARED_DIR)
1052  tmp = SHARED_DIR;
1053  AppendPathSeparator(tmp);
1054  _searchpaths[SP_SHARED_DIR] = tmp;
1055 #else
1056  _searchpaths[SP_SHARED_DIR].clear();
1057 #endif
1058 
1059  char cwd[MAX_PATH];
1060  if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
1061 
1062  if (_config_file.empty()) {
1063  /* Get the path to working directory of OpenTTD. */
1064  tmp = cwd;
1065  AppendPathSeparator(tmp);
1067 
1069  } else {
1070  /* Use the folder of the config file as working directory. */
1071  size_t end = _config_file.find_last_of(PATHSEPCHAR);
1072  if (end == std::string::npos) {
1073  /* _config_file is not in a folder, so use current directory. */
1074  tmp = cwd;
1075  AppendPathSeparator(tmp);
1077  } else {
1078  _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
1079  }
1080  }
1081 
1082  /* Change the working directory to that one of the executable */
1084  char buf[MAX_PATH];
1085  if (getcwd(buf, lengthof(buf)) == nullptr) {
1086  tmp.clear();
1087  } else {
1088  tmp = buf;
1089  }
1090  AppendPathSeparator(tmp);
1091  _searchpaths[SP_BINARY_DIR] = tmp;
1092  } else {
1093  _searchpaths[SP_BINARY_DIR].clear();
1094  }
1095 
1096  if (cwd[0] != '\0') {
1097  /* Go back to the current working directory. */
1098  if (chdir(cwd) != 0) {
1099  DEBUG(misc, 0, "Failed to return to working directory!");
1100  }
1101  }
1102 
1103 #if !defined(GLOBAL_DATA_DIR)
1105 #else
1106  tmp = GLOBAL_DATA_DIR;
1107  AppendPathSeparator(tmp);
1109 #endif
1110 #ifdef WITH_COCOA
1111 extern void CocoaSetApplicationBundleDir();
1112  CocoaSetApplicationBundleDir();
1113 #else
1115 #endif
1116 }
1117 #endif /* defined(_WIN32) */
1118 
1119 std::string _personal_dir;
1120 
1127 void DeterminePaths(const char *exe)
1128 {
1129  DetermineBasePaths(exe);
1130 
1131 #ifdef USE_XDG
1132  std::string config_home;
1133  const std::string homedir = GetHomeDir();
1134  const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
1135  if (xdg_config_home != nullptr) {
1136  config_home = xdg_config_home;
1137  config_home += PATHSEP;
1138  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1139  } else if (!homedir.empty()) {
1140  /* Defaults to ~/.config */
1141  config_home = homedir;
1142  config_home += PATHSEP ".config" PATHSEP;
1143  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1144  }
1145  AppendPathSeparator(config_home);
1146 #endif
1147 
1148  Searchpath sp;
1149  FOR_ALL_SEARCHPATHS(sp) {
1150  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1151  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp].c_str());
1152  }
1153 
1154  std::string config_dir;
1155  if (!_config_file.empty()) {
1156  config_dir = _searchpaths[SP_WORKING_DIR];
1157  } else {
1158  std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
1159  if (!personal_dir.empty()) {
1160  auto end = personal_dir.find_last_of(PATHSEPCHAR);
1161  if (end != std::string::npos) personal_dir.erase(end + 1);
1162  config_dir = personal_dir;
1163  } else {
1164 #ifdef USE_XDG
1165  /* No previous configuration file found. Use the configuration folder from XDG. */
1166  config_dir = config_home;
1167 #else
1168  static const Searchpath new_openttd_cfg_order[] = {
1170  };
1171 
1172  config_dir.clear();
1173  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1174  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1175  config_dir = _searchpaths[new_openttd_cfg_order[i]];
1176  break;
1177  }
1178  }
1179 #endif
1180  }
1181  _config_file = config_dir + "openttd.cfg";
1182  }
1183 
1184  DEBUG(misc, 3, "%s found as config directory", config_dir.c_str());
1185 
1186  _highscore_file = config_dir + "hs.dat";
1187  extern std::string _hotkeys_file;
1188  _hotkeys_file = config_dir + "hotkeys.cfg";
1189  extern std::string _windows_file;
1190  _windows_file = config_dir + "windows.cfg";
1191 
1192 #ifdef USE_XDG
1193  if (config_dir == config_home) {
1194  /* We are using the XDG configuration home for the config file,
1195  * then store the rest in the XDG data home folder. */
1196  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1197  } else
1198 #endif
1199  {
1200  _personal_dir = config_dir;
1201  }
1202 
1203  /* Make the necessary folders */
1204  FioCreateDirectory(config_dir);
1205 #if defined(WITH_PERSONAL_DIR)
1207 #endif
1208 
1209  DEBUG(misc, 3, "%s found as personal directory", _personal_dir.c_str());
1210 
1211  static const Subdirectory default_subdirs[] = {
1213  };
1214 
1215  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1216  FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1217  }
1218 
1219  /* If we have network we make a directory for the autodownloading of content */
1220  _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1222 
1223  /* Create the directory for each of the types of content */
1225  for (uint i = 0; i < lengthof(dirs); i++) {
1226  FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1227  }
1228 
1229  extern std::string _log_file;
1230  _log_file = _personal_dir + "openttd.log";
1231 }
1232 
1237 void SanitizeFilename(char *filename)
1238 {
1239  for (; *filename != '\0'; filename++) {
1240  switch (*filename) {
1241  /* The following characters are not allowed in filenames
1242  * on at least one of the supported operating systems: */
1243  case ':': case '\\': case '*': case '?': case '/':
1244  case '<': case '>': case '|': case '"':
1245  *filename = '_';
1246  break;
1247  }
1248  }
1249 }
1250 
1259 std::unique_ptr<char> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1260 {
1261  FILE *in = fopen(filename.c_str(), "rb");
1262  if (in == nullptr) return nullptr;
1263 
1264  FileCloser fc(in);
1265 
1266  fseek(in, 0, SEEK_END);
1267  size_t len = ftell(in);
1268  fseek(in, 0, SEEK_SET);
1269  if (len > maxsize) return nullptr;
1270 
1271  /* std::unique_ptr assumes new/delete unless a custom deleter is supplied.
1272  * As we don't want to have to carry that deleter all over the place, use
1273  * new directly to allocate the memory instead of malloc. */
1274  std::unique_ptr<char> mem(static_cast<char *>(::operator new(len + 1)));
1275 
1276  mem.get()[len] = 0;
1277  if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1278 
1279  lenp = len;
1280  return mem;
1281 }
1282 
1289 static bool MatchesExtension(const char *extension, const char *filename)
1290 {
1291  if (extension == nullptr) return true;
1292 
1293  const char *ext = strrchr(filename, extension[0]);
1294  return ext != nullptr && strcasecmp(ext, extension) == 0;
1295 }
1296 
1306 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1307 {
1308  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1309 
1310  uint num = 0;
1311  struct stat sb;
1312  struct dirent *dirent;
1313  DIR *dir;
1314 
1315  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1316 
1317  while ((dirent = readdir(dir)) != nullptr) {
1318  const char *d_name = FS2OTTD(dirent->d_name);
1319 
1320  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1321 
1322  std::string filename(path);
1323  filename += d_name;
1324 
1325  if (S_ISDIR(sb.st_mode)) {
1326  /* Directory */
1327  if (!recursive) continue;
1328  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1329  AppendPathSeparator(filename);
1330  num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1331  } else if (S_ISREG(sb.st_mode)) {
1332  /* File */
1333  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1334  }
1335  }
1336 
1337  closedir(dir);
1338 
1339  return num;
1340 }
1341 
1348 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1349 {
1350  uint num = 0;
1351  const auto &filename = (*tar).first;
1352 
1353  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1354 
1355  return num;
1356 }
1357 
1367 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1368 {
1369  this->subdir = sd;
1370 
1371  Searchpath sp;
1372  TarFileList::iterator tar;
1373  uint num = 0;
1374 
1375  FOR_ALL_SEARCHPATHS(sp) {
1376  /* Don't search in the working directory */
1377  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1378 
1379  std::string path = FioGetDirectory(sp, sd);
1380  num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1381  }
1382 
1383  if (tars && sd != NO_DIRECTORY) {
1384  FOR_ALL_TARS(tar, sd) {
1385  num += ScanTar(this, extension, tar);
1386  }
1387  }
1388 
1389  switch (sd) {
1390  case BASESET_DIR:
1391  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1392  FALLTHROUGH;
1393  case NEWGRF_DIR:
1394  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1395  break;
1396 
1397  default: break;
1398  }
1399 
1400  return num;
1401 }
1402 
1411 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1412 {
1413  std::string path(directory);
1414  AppendPathSeparator(path);
1415  return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1416 }
ReadFileToMem
std::unique_ptr< char > ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1259
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1289
SP_AUTODOWNLOAD_DIR
@ SP_AUTODOWNLOAD_DIR
Search within the autodownload directory.
Definition: fileio_type.h:142
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:845
SAVE_DIR
@ SAVE_DIR
Base directory for all savegames.
Definition: fileio_type.h:110
FioReadBlock
void FioReadBlock(void *ptr, size_t size)
Read a block.
Definition: fileio.cpp:161
usererror
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:100
_tar_linklist
static TarLinkList _tar_linklist[NUM_SUBDIRS]
List of directory links.
Definition: fileio.cpp:248
FioReadWord
uint16 FioReadWord()
Read a word (16 bits) from the file (in low endian format).
Definition: fileio.cpp:140
FS2OTTD
const char * FS2OTTD(const wchar_t *name)
Convert to OpenTTD's encoding from wide characters.
Definition: win32.cpp:565
_personal_dir
std::string _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:1119
strtolower
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
Definition: string.cpp:354
SP_PERSONAL_DIR
@ SP_PERSONAL_DIR
Search in the personal directory.
Definition: fileio_type.h:137
FioOpenFile
void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir)
Open a slotted file.
Definition: fileio.cpp:196
Fio::pos
size_t pos
current (system) position in file
Definition: fileio.cpp:39
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:569
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:1367
DeterminePaths
void DeterminePaths(const char *exe)
Acquire the base paths (personal dir and game data dir), fill all other paths (save dir,...
Definition: fileio.cpp:1127
Fio::buffer_start
byte buffer_start[FIO_BUFFER_SIZE]
local buffer when read from file
Definition: fileio.cpp:38
HEIGHTMAP_DIR
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:113
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
FOR_ALL_SEARCHPATHS
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
Definition: fileio_func.h:37
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
Fio::filenames
std::array< std::string, MAX_FILE_SLOTS > filenames
array of filenames we (should) have open
Definition: fileio.cpp:43
Fio::buffer_end
byte * buffer_end
position pointer in local buffer and last valid byte of buffer
Definition: fileio.cpp:37
fios.h
FioFOpenFileTar
FILE * FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
Opens a file from inside a tar archive.
Definition: fileio.cpp:386
OLD_GM_DIR
@ OLD_GM_DIR
Old subdirectory for the music.
Definition: fileio_type.h:114
FioReadDword
uint32 FioReadDword()
Read a double word (32 bits) from the file (in low endian format).
Definition: fileio.cpp:150
FileCloser
Auto-close a file upon scope exit.
Definition: fileio_func.h:140
FioSkipBytes
void FioSkipBytes(int n)
Skip n bytes ahead in the file.
Definition: fileio.cpp:124
ScanTar
static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
Scan the given tar and add graphics sets when it finds one.
Definition: fileio.cpp:1348
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:235
Fio::cur_fh
FILE * cur_fh
current file handle
Definition: fileio.cpp:40
AI_DIR
@ AI_DIR
Subdirectory for all AI files.
Definition: fileio_type.h:119
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
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:406
_do_scan_working_directory
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:50
_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:280
Fio::shortnames
std::array< std::string, MAX_FILE_SLOTS > shortnames
array of short names for spriteloader's use
Definition: fileio.cpp:44
FioCloseAll
void FioCloseAll()
Close all slotted open files.
Definition: fileio.cpp:183
_fio
static Fio _fio
Fio instance.
Definition: fileio.cpp:47
ChangeWorkingDirectoryToExecutable
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:924
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
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:61
TarScanner::NEWGRF
@ NEWGRF
Scan for non-base sets.
Definition: fileio_func.h:88
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:490
SP_SHARED_DIR
@ SP_SHARED_DIR
Search in the shared directory, like 'Shared Files' under Windows.
Definition: fileio_type.h:138
FioCloseFile
static void FioCloseFile(int slot)
Close the file at the given slot number.
Definition: fileio.cpp:171
stdafx.h
FioSeekTo
void FioSeekTo(size_t pos, int mode)
Seek in the current file.
Definition: fileio.cpp:79
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:520
TarScanner::GAME
@ GAME
Scan for game scripts.
Definition: fileio_func.h:91
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:617
FioGetPos
size_t FioGetPos()
Get position in the current file.
Definition: fileio.cpp:59
FioReadByte
byte FioReadByte()
Read a byte from the file.
Definition: fileio.cpp:107
string_func.h
_windows_file
std::string _windows_file
Config file to store WindowDesc.
Definition: window.cpp:89
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:90
IsValidSearchPath
bool IsValidSearchPath(Searchpath sp)
Checks whether the given search path is a valid search path.
Definition: fileio.cpp:255
_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:266
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:89
GetHomeDir
static std::string GetHomeDir()
Gets the home directory of the user.
Definition: fileio.cpp:980
Fio::handles
std::array< FILE *, MAX_FILE_SLOTS > handles
array of file handles we can have open
Definition: fileio.cpp:42
DIR
Definition: win32.cpp:92
SanitizeFilename
void SanitizeFilename(char *filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1237
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:1306
SimplifyFileName
static void SimplifyFileName(char *name)
Simplify filenames from tars.
Definition: fileio.cpp:553
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:1000
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:81
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:367
FileScanner
Helper for scanning for files with a given name.
Definition: fileio_func.h:59
TarScanner::BASESET
@ BASESET
Scan for base sets.
Definition: fileio_func.h:87
FioSeekToFile
void FioSeekToFile(uint8 slot, size_t pos)
Switch to a different file and seek to a position.
Definition: fileio.cpp:94
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:299
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:133
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:85
FIO_BUFFER_SIZE
#define FIO_BUFFER_SIZE
Size of the Fio data buffer.
Definition: fileio.cpp:33
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:383
Fio
Structure for keeping several open files with just one data buffer.
Definition: fileio.cpp:36
OTTD2FS
const wchar_t * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to wide characters.
Definition: win32.cpp:580
DoScanWorkingDirectory
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:958
Fio::filename
std::string filename
current filename
Definition: fileio.cpp:41
FioFCloseFile
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:288
debug.h
FioGetFilename
const char * FioGetFilename(uint8 slot)
Get the filename associated with a slot.
Definition: fileio.cpp:69
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:83
TarFileListEntry
Definition: tar_type.h:20