OpenTTD Source  1.11.2
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 if (token == ".") {
433  /* Do nothing. "." means current folder, but you can create tar files with "." in the path.
434  * This confuses our file resolver. So, act like this folder doesn't exist. */
435  } else {
436  tokens.push_back(token);
437  }
438  }
439 
440  resolved_name.clear();
441  bool first = true;
442  for (const std::string &token : tokens) {
443  if (!first) {
444  resolved_name += PATHSEP;
445  }
446  resolved_name += token;
447  first = false;
448  }
449 
450  /* Resolve ONE directory link */
451  for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
452  const std::string &src = link->first;
453  size_t len = src.length();
454  if (resolved_name.length() >= len && resolved_name[len - 1] == PATHSEPCHAR && src.compare(0, len, resolved_name, 0, len) == 0) {
455  /* Apply link */
456  resolved_name.replace(0, len, link->second);
457  break; // Only resolve one level
458  }
459  }
460 
461  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
462  if (it != _tar_filelist[subdir].end()) {
463  f = FioFOpenFileTar(it->second, filesize);
464  }
465  }
466 
467  /* Sometimes a full path is given. To support
468  * the 'subdirectory' must be 'removed'. */
469  if (f == nullptr && subdir != NO_DIRECTORY) {
470  switch (subdir) {
471  case BASESET_DIR:
472  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
473  if (f != nullptr) break;
474  FALLTHROUGH;
475  case NEWGRF_DIR:
476  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
477  break;
478 
479  default:
480  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
481  break;
482  }
483  }
484 
485  return f;
486 }
487 
493 void FioCreateDirectory(const std::string &name)
494 {
495  auto p = name.find_last_of(PATHSEPCHAR);
496  if (p != std::string::npos) {
497  std::string dirname = name.substr(0, p);
498  DIR *dir = ttd_opendir(dirname.c_str());
499  if (dir == nullptr) {
500  FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
501  } else {
502  closedir(dir);
503  }
504  }
505 
506  /* Ignore directory creation errors; they'll surface later on, and most
507  * of the time they are 'directory already exists' errors anyhow. */
508 #if defined(_WIN32)
509  CreateDirectory(OTTD2FS(name.c_str()), nullptr);
510 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
511  mkdir(OTTD2FS(name.c_str()));
512 #else
513  mkdir(OTTD2FS(name.c_str()), 0755);
514 #endif
515 }
516 
523 void AppendPathSeparator(std::string &buf)
524 {
525  if (buf.empty()) return;
526 
527  if (buf.back() != PATHSEPCHAR) buf.push_back(PATHSEPCHAR);
528 }
529 
530 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
531 {
532  std::string src = srcParam;
533  std::string dest = destParam;
534  /* Tar internals assume lowercase */
535  std::transform(src.begin(), src.end(), src.begin(), tolower);
536  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
537 
538  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
539  if (dest_file != _tar_filelist[subdir].end()) {
540  /* Link to file. Process the link like the destination file. */
541  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
542  } else {
543  /* Destination file not found. Assume 'link to directory'
544  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
545  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
546  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
547  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
548  }
549 }
550 
556 static void SimplifyFileName(char *name)
557 {
558  /* Force lowercase */
559  strtolower(name);
560 
561  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
562 #if (PATHSEPCHAR != '/')
563  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
564 #endif
565 }
566 
573 {
574  _tar_filelist[sd].clear();
575  _tar_list[sd].clear();
576  uint num = this->Scan(".tar", sd, false);
577  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
578  return num;
579 }
580 
581 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
582 {
583  DEBUG(misc, 1, "Scanning for tars");
584  TarScanner fs;
585  uint num = 0;
586  if (mode & TarScanner::BASESET) {
587  num += fs.DoScan(BASESET_DIR);
588  }
589  if (mode & TarScanner::NEWGRF) {
590  num += fs.DoScan(NEWGRF_DIR);
591  }
592  if (mode & TarScanner::AI) {
593  num += fs.DoScan(AI_DIR);
594  num += fs.DoScan(AI_LIBRARY_DIR);
595  }
596  if (mode & TarScanner::GAME) {
597  num += fs.DoScan(GAME_DIR);
598  num += fs.DoScan(GAME_LIBRARY_DIR);
599  }
600  if (mode & TarScanner::SCENARIO) {
601  num += fs.DoScan(SCENARIO_DIR);
602  num += fs.DoScan(HEIGHTMAP_DIR);
603  }
604  DEBUG(misc, 1, "Scan complete, found %d files", num);
605  return num;
606 }
607 
614 bool TarScanner::AddFile(Subdirectory sd, const std::string &filename)
615 {
616  this->subdir = sd;
617  return this->AddFile(filename, 0);
618 }
619 
620 bool TarScanner::AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename)
621 {
622  /* No tar within tar. */
623  assert(tar_filename.empty());
624 
625  /* The TAR-header, repeated for every file */
626  struct TarHeader {
627  char name[100];
628  char mode[8];
629  char uid[8];
630  char gid[8];
631  char size[12];
632  char mtime[12];
633  char chksum[8];
634  char typeflag;
635  char linkname[100];
636  char magic[6];
637  char version[2];
638  char uname[32];
639  char gname[32];
640  char devmajor[8];
641  char devminor[8];
642  char prefix[155];
643 
644  char unused[12];
645  };
646 
647  /* Check if we already seen this file */
648  TarList::iterator it = _tar_list[this->subdir].find(filename);
649  if (it != _tar_list[this->subdir].end()) return false;
650 
651  FILE *f = fopen(filename.c_str(), "rb");
652  /* Although the file has been found there can be
653  * a number of reasons we cannot open the file.
654  * Most common case is when we simply have not
655  * been given read access. */
656  if (f == nullptr) return false;
657 
658  _tar_list[this->subdir][filename] = std::string{};
659 
660  TarLinkList links;
661 
662  TarHeader th;
663  char buf[sizeof(th.name) + 1], *end;
664  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
665  char link[sizeof(th.linkname) + 1];
666  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
667  size_t num = 0, pos = 0;
668 
669  /* Make a char of 512 empty bytes */
670  char empty[512];
671  memset(&empty[0], 0, sizeof(empty));
672 
673  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
674  size_t num_bytes_read = fread(&th, 1, 512, f);
675  if (num_bytes_read != 512) break;
676  pos += num_bytes_read;
677 
678  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
679  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
680  /* If we have only zeros in the block, it can be an end-of-file indicator */
681  if (memcmp(&th, &empty[0], 512) == 0) continue;
682 
683  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename.c_str());
684  fclose(f);
685  return false;
686  }
687 
688  name[0] = '\0';
689 
690  /* The prefix contains the directory-name */
691  if (th.prefix[0] != '\0') {
692  strecpy(name, th.prefix, lastof(name));
693  strecat(name, PATHSEP, lastof(name));
694  }
695 
696  /* Copy the name of the file in a safe way at the end of 'name' */
697  strecat(name, th.name, lastof(name));
698 
699  /* Calculate the size of the file.. for some strange reason this is stored as a string */
700  strecpy(buf, th.size, lastof(buf));
701  size_t skip = strtoul(buf, &end, 8);
702 
703  switch (th.typeflag) {
704  case '\0':
705  case '0': { // regular file
706  /* Ignore empty files */
707  if (skip == 0) break;
708 
709  if (strlen(name) == 0) break;
710 
711  /* Store this entry in the list */
712  TarFileListEntry entry;
713  entry.tar_filename = filename;
714  entry.size = skip;
715  entry.position = pos;
716 
717  /* Convert to lowercase and our PATHSEPCHAR */
718  SimplifyFileName(name);
719 
720  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
721  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
722 
723  break;
724  }
725 
726  case '1': // hard links
727  case '2': { // symbolic links
728  /* Copy the destination of the link in a safe way at the end of 'linkname' */
729  strecpy(link, th.linkname, lastof(link));
730 
731  if (strlen(name) == 0 || strlen(link) == 0) break;
732 
733  /* Convert to lowercase and our PATHSEPCHAR */
734  SimplifyFileName(name);
735  SimplifyFileName(link);
736 
737  /* Only allow relative links */
738  if (link[0] == PATHSEPCHAR) {
739  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
740  break;
741  }
742 
743  /* Process relative path.
744  * Note: The destination of links must not contain any directory-links. */
745  strecpy(dest, name, lastof(dest));
746  char *destpos = strrchr(dest, PATHSEPCHAR);
747  if (destpos == nullptr) destpos = dest;
748  *destpos = '\0';
749 
750  char *pos = link;
751  while (*pos != '\0') {
752  char *next = strchr(pos, PATHSEPCHAR);
753  if (next == nullptr) {
754  next = pos + strlen(pos);
755  } else {
756  /* Terminate the substring up to the path separator character. */
757  *next++= '\0';
758  }
759 
760  if (strcmp(pos, ".") == 0) {
761  /* Skip '.' (current dir) */
762  } else if (strcmp(pos, "..") == 0) {
763  /* level up */
764  if (dest[0] == '\0') {
765  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
766  break;
767  }
768 
769  /* Truncate 'dest' after last PATHSEPCHAR.
770  * This assumes that the truncated part is a real directory and not a link. */
771  destpos = strrchr(dest, PATHSEPCHAR);
772  if (destpos == nullptr) destpos = dest;
773  *destpos = '\0';
774  } else {
775  /* Append at end of 'dest' */
776  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
777  destpos = strecpy(destpos, pos, lastof(dest));
778  }
779 
780  if (destpos >= lastof(dest)) {
781  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename.c_str());
782  fclose(f);
783  return false;
784  }
785 
786  pos = next;
787  }
788 
789  /* Store links in temporary list */
790  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
791  links.insert(TarLinkList::value_type(name, dest));
792 
793  break;
794  }
795 
796  case '5': // directory
797  /* Convert to lowercase and our PATHSEPCHAR */
798  SimplifyFileName(name);
799 
800  /* Store the first directory name we detect */
801  DEBUG(misc, 6, "Found dir in tar: %s", name);
802  if (_tar_list[this->subdir][filename].empty()) _tar_list[this->subdir][filename] = name;
803  break;
804 
805  default:
806  /* Ignore other types */
807  break;
808  }
809 
810  /* Skip to the next block.. */
811  skip = Align(skip, 512);
812  if (fseek(f, skip, SEEK_CUR) < 0) {
813  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename.c_str());
814  fclose(f);
815  return false;
816  }
817  pos += skip;
818  }
819 
820  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename.c_str(), num);
821  fclose(f);
822 
823  /* Resolve file links and store directory links.
824  * We restrict usage of links to two cases:
825  * 1) Links to directories:
826  * Both the source path and the destination path must NOT contain any further links.
827  * When resolving files at most one directory link is resolved.
828  * 2) Links to files:
829  * The destination path must NOT contain any links.
830  * The source path may contain one directory link.
831  */
832  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
833  const std::string &src = link->first;
834  const std::string &dest = link->second;
835  TarAddLink(src, dest, this->subdir);
836  }
837 
838  return true;
839 }
840 
848 bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
849 {
850  TarList::iterator it = _tar_list[subdir].find(tar_filename);
851  /* We don't know the file. */
852  if (it == _tar_list[subdir].end()) return false;
853 
854  const auto &dirname = (*it).second;
855 
856  /* The file doesn't have a sub directory! */
857  if (dirname.empty()) {
858  DEBUG(misc, 1, "Extracting %s failed; archive rejected, the contents must be in a sub directory", tar_filename.c_str());
859  return false;
860  }
861 
862  std::string filename = tar_filename;
863  auto p = filename.find_last_of(PATHSEPCHAR);
864  /* The file's path does not have a separator? */
865  if (p == std::string::npos) return false;
866 
867  filename.replace(p + 1, std::string::npos, dirname);
868  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename.c_str(), filename.c_str());
869  FioCreateDirectory(filename);
870 
871  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
872  if (tar_filename != it2->second.tar_filename) continue;
873 
874  filename.replace(p + 1, std::string::npos, it2->first);
875 
876  DEBUG(misc, 9, " extracting %s", filename.c_str());
877 
878  /* First open the file in the .tar. */
879  size_t to_copy = 0;
880  std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2->second, &to_copy));
881  if (!in) {
882  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), tar_filename.c_str());
883  return false;
884  }
885 
886  /* Now open the 'output' file. */
887  std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
888  if (!out) {
889  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename.c_str(), filename.c_str());
890  return false;
891  }
892 
893  /* Now read from the tar and write it into the file. */
894  char buffer[4096];
895  size_t read;
896  for (; to_copy != 0; to_copy -= read) {
897  read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
898  if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
899  }
900 
901  if (to_copy != 0) {
902  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename.c_str(), (int)to_copy);
903  return false;
904  }
905  }
906 
907  DEBUG(misc, 9, " extraction successful");
908  return true;
909 }
910 
911 #if defined(_WIN32)
912 
917 extern void DetermineBasePaths(const char *exe);
918 #else /* defined(_WIN32) */
919 
927 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
928 {
929  char tmp[MAX_PATH];
930  strecpy(tmp, exe, lastof(tmp));
931 
932  bool success = false;
933 #ifdef WITH_COCOA
934  char *app_bundle = strchr(tmp, '.');
935  while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
936 
937  if (app_bundle != nullptr) *app_bundle = '\0';
938 #endif /* WITH_COCOA */
939  char *s = strrchr(tmp, PATHSEPCHAR);
940  if (s != nullptr) {
941  *s = '\0';
942  if (chdir(tmp) != 0) {
943  DEBUG(misc, 0, "Directory with the binary does not exist?");
944  } else {
945  success = true;
946  }
947  }
948  return success;
949 }
950 
962 {
963  /* No working directory, so nothing to do. */
964  if (_searchpaths[SP_WORKING_DIR].empty()) return false;
965 
966  /* Working directory is root, so do nothing. */
967  if (_searchpaths[SP_WORKING_DIR] == PATHSEP) return false;
968 
969  /* No personal/home directory, so the working directory won't be that. */
970  if (_searchpaths[SP_PERSONAL_DIR].empty()) return true;
971 
972  std::string tmp = _searchpaths[SP_WORKING_DIR] + PERSONAL_DIR;
973  AppendPathSeparator(tmp);
974 
975  return _searchpaths[SP_PERSONAL_DIR] != tmp;
976 }
977 
983 static std::string GetHomeDir()
984 {
985 #ifdef __HAIKU__
986  BPath path;
987  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
988  return std::string(path.Path());
989 #else
990  const char *home_env = getenv("HOME"); // Stack var, shouldn't be freed
991  if (home_env != nullptr) return std::string(home_env);
992 
993  const struct passwd *pw = getpwuid(getuid());
994  if (pw != nullptr) return std::string(pw->pw_dir);
995 #endif
996  return {};
997 }
998 
1003 void DetermineBasePaths(const char *exe)
1004 {
1005  std::string tmp;
1006  const std::string homedir = GetHomeDir();
1007 #ifdef USE_XDG
1008  const char *xdg_data_home = getenv("XDG_DATA_HOME");
1009  if (xdg_data_home != nullptr) {
1010  tmp = xdg_data_home;
1011  tmp += PATHSEP;
1012  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1013  AppendPathSeparator(tmp);
1014  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1015 
1016  tmp += "content_download";
1017  AppendPathSeparator(tmp);
1019  } else if (!homedir.empty()) {
1020  tmp = homedir;
1021  tmp += PATHSEP ".local" PATHSEP "share" PATHSEP;
1022  tmp += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1023  AppendPathSeparator(tmp);
1024  _searchpaths[SP_PERSONAL_DIR_XDG] = tmp;
1025 
1026  tmp += "content_download";
1027  AppendPathSeparator(tmp);
1029  } else {
1030  _searchpaths[SP_PERSONAL_DIR_XDG].clear();
1032  }
1033 #endif
1034 
1035 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1036  _searchpaths[SP_PERSONAL_DIR].clear();
1037 #else
1038  if (!homedir.empty()) {
1039  tmp = homedir;
1040  tmp += PATHSEP;
1041  tmp += PERSONAL_DIR;
1042  AppendPathSeparator(tmp);
1044 
1045  tmp += "content_download";
1046  AppendPathSeparator(tmp);
1048  } else {
1049  _searchpaths[SP_PERSONAL_DIR].clear();
1051  }
1052 #endif
1053 
1054 #if defined(WITH_SHARED_DIR)
1055  tmp = SHARED_DIR;
1056  AppendPathSeparator(tmp);
1057  _searchpaths[SP_SHARED_DIR] = tmp;
1058 #else
1059  _searchpaths[SP_SHARED_DIR].clear();
1060 #endif
1061 
1062  char cwd[MAX_PATH];
1063  if (getcwd(cwd, MAX_PATH) == nullptr) *cwd = '\0';
1064 
1065  if (_config_file.empty()) {
1066  /* Get the path to working directory of OpenTTD. */
1067  tmp = cwd;
1068  AppendPathSeparator(tmp);
1070 
1072  } else {
1073  /* Use the folder of the config file as working directory. */
1074  size_t end = _config_file.find_last_of(PATHSEPCHAR);
1075  if (end == std::string::npos) {
1076  /* _config_file is not in a folder, so use current directory. */
1077  tmp = cwd;
1078  AppendPathSeparator(tmp);
1080  } else {
1081  _searchpaths[SP_WORKING_DIR] = _config_file.substr(0, end + 1);
1082  }
1083  }
1084 
1085  /* Change the working directory to that one of the executable */
1087  char buf[MAX_PATH];
1088  if (getcwd(buf, lengthof(buf)) == nullptr) {
1089  tmp.clear();
1090  } else {
1091  tmp = buf;
1092  }
1093  AppendPathSeparator(tmp);
1094  _searchpaths[SP_BINARY_DIR] = tmp;
1095  } else {
1096  _searchpaths[SP_BINARY_DIR].clear();
1097  }
1098 
1099  if (cwd[0] != '\0') {
1100  /* Go back to the current working directory. */
1101  if (chdir(cwd) != 0) {
1102  DEBUG(misc, 0, "Failed to return to working directory!");
1103  }
1104  }
1105 
1106 #if !defined(GLOBAL_DATA_DIR)
1108 #else
1109  tmp = GLOBAL_DATA_DIR;
1110  AppendPathSeparator(tmp);
1112 #endif
1113 #ifdef WITH_COCOA
1114 extern void CocoaSetApplicationBundleDir();
1115  CocoaSetApplicationBundleDir();
1116 #else
1118 #endif
1119 }
1120 #endif /* defined(_WIN32) */
1121 
1122 std::string _personal_dir;
1123 
1130 void DeterminePaths(const char *exe)
1131 {
1132  DetermineBasePaths(exe);
1133 
1134 #ifdef USE_XDG
1135  std::string config_home;
1136  const std::string homedir = GetHomeDir();
1137  const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
1138  if (xdg_config_home != nullptr) {
1139  config_home = xdg_config_home;
1140  config_home += PATHSEP;
1141  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1142  } else if (!homedir.empty()) {
1143  /* Defaults to ~/.config */
1144  config_home = homedir;
1145  config_home += PATHSEP ".config" PATHSEP;
1146  config_home += PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR;
1147  }
1148  AppendPathSeparator(config_home);
1149 #endif
1150 
1151  Searchpath sp;
1152  FOR_ALL_SEARCHPATHS(sp) {
1153  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1154  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp].c_str());
1155  }
1156 
1157  std::string config_dir;
1158  if (!_config_file.empty()) {
1159  config_dir = _searchpaths[SP_WORKING_DIR];
1160  } else {
1161  std::string personal_dir = FioFindFullPath(BASE_DIR, "openttd.cfg");
1162  if (!personal_dir.empty()) {
1163  auto end = personal_dir.find_last_of(PATHSEPCHAR);
1164  if (end != std::string::npos) personal_dir.erase(end + 1);
1165  config_dir = personal_dir;
1166  } else {
1167 #ifdef USE_XDG
1168  /* No previous configuration file found. Use the configuration folder from XDG. */
1169  config_dir = config_home;
1170 #else
1171  static const Searchpath new_openttd_cfg_order[] = {
1173  };
1174 
1175  config_dir.clear();
1176  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1177  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1178  config_dir = _searchpaths[new_openttd_cfg_order[i]];
1179  break;
1180  }
1181  }
1182 #endif
1183  }
1184  _config_file = config_dir + "openttd.cfg";
1185  }
1186 
1187  DEBUG(misc, 3, "%s found as config directory", config_dir.c_str());
1188 
1189  _highscore_file = config_dir + "hs.dat";
1190  extern std::string _hotkeys_file;
1191  _hotkeys_file = config_dir + "hotkeys.cfg";
1192  extern std::string _windows_file;
1193  _windows_file = config_dir + "windows.cfg";
1194 
1195 #ifdef USE_XDG
1196  if (config_dir == config_home) {
1197  /* We are using the XDG configuration home for the config file,
1198  * then store the rest in the XDG data home folder. */
1199  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1200  } else
1201 #endif
1202  {
1203  _personal_dir = config_dir;
1204  }
1205 
1206  /* Make the necessary folders */
1207  FioCreateDirectory(config_dir);
1208 #if defined(WITH_PERSONAL_DIR)
1210 #endif
1211 
1212  DEBUG(misc, 3, "%s found as personal directory", _personal_dir.c_str());
1213 
1214  static const Subdirectory default_subdirs[] = {
1216  };
1217 
1218  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1219  FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
1220  }
1221 
1222  /* If we have network we make a directory for the autodownloading of content */
1223  _searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
1225 
1226  /* Create the directory for each of the types of content */
1228  for (uint i = 0; i < lengthof(dirs); i++) {
1229  FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
1230  }
1231 
1232  extern std::string _log_file;
1233  _log_file = _personal_dir + "openttd.log";
1234 }
1235 
1240 void SanitizeFilename(char *filename)
1241 {
1242  for (; *filename != '\0'; filename++) {
1243  switch (*filename) {
1244  /* The following characters are not allowed in filenames
1245  * on at least one of the supported operating systems: */
1246  case ':': case '\\': case '*': case '?': case '/':
1247  case '<': case '>': case '|': case '"':
1248  *filename = '_';
1249  break;
1250  }
1251  }
1252 }
1253 
1262 std::unique_ptr<char> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
1263 {
1264  FILE *in = fopen(filename.c_str(), "rb");
1265  if (in == nullptr) return nullptr;
1266 
1267  FileCloser fc(in);
1268 
1269  fseek(in, 0, SEEK_END);
1270  size_t len = ftell(in);
1271  fseek(in, 0, SEEK_SET);
1272  if (len > maxsize) return nullptr;
1273 
1274  /* std::unique_ptr assumes new/delete unless a custom deleter is supplied.
1275  * As we don't want to have to carry that deleter all over the place, use
1276  * new directly to allocate the memory instead of malloc. */
1277  std::unique_ptr<char> mem(static_cast<char *>(::operator new(len + 1)));
1278 
1279  mem.get()[len] = 0;
1280  if (fread(mem.get(), len, 1, in) != 1) return nullptr;
1281 
1282  lenp = len;
1283  return mem;
1284 }
1285 
1292 static bool MatchesExtension(const char *extension, const char *filename)
1293 {
1294  if (extension == nullptr) return true;
1295 
1296  const char *ext = strrchr(filename, extension[0]);
1297  return ext != nullptr && strcasecmp(ext, extension) == 0;
1298 }
1299 
1309 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1310 {
1311  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1312 
1313  uint num = 0;
1314  struct stat sb;
1315  struct dirent *dirent;
1316  DIR *dir;
1317 
1318  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1319 
1320  while ((dirent = readdir(dir)) != nullptr) {
1321  const char *d_name = FS2OTTD(dirent->d_name);
1322 
1323  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1324 
1325  std::string filename(path);
1326  filename += d_name;
1327 
1328  if (S_ISDIR(sb.st_mode)) {
1329  /* Directory */
1330  if (!recursive) continue;
1331  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1332  AppendPathSeparator(filename);
1333  num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive);
1334  } else if (S_ISREG(sb.st_mode)) {
1335  /* File */
1336  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, basepath_length, {})) num++;
1337  }
1338  }
1339 
1340  closedir(dir);
1341 
1342  return num;
1343 }
1344 
1351 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1352 {
1353  uint num = 0;
1354  const auto &filename = (*tar).first;
1355 
1356  if (MatchesExtension(extension, filename.c_str()) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1357 
1358  return num;
1359 }
1360 
1370 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1371 {
1372  this->subdir = sd;
1373 
1374  Searchpath sp;
1375  TarFileList::iterator tar;
1376  uint num = 0;
1377 
1378  FOR_ALL_SEARCHPATHS(sp) {
1379  /* Don't search in the working directory */
1380  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1381 
1382  std::string path = FioGetDirectory(sp, sd);
1383  num += ScanPath(this, extension, path.c_str(), path.size(), recursive);
1384  }
1385 
1386  if (tars && sd != NO_DIRECTORY) {
1387  FOR_ALL_TARS(tar, sd) {
1388  num += ScanTar(this, extension, tar);
1389  }
1390  }
1391 
1392  switch (sd) {
1393  case BASESET_DIR:
1394  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1395  FALLTHROUGH;
1396  case NEWGRF_DIR:
1397  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1398  break;
1399 
1400  default: break;
1401  }
1402 
1403  return num;
1404 }
1405 
1414 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1415 {
1416  std::string path(directory);
1417  AppendPathSeparator(path);
1418  return ScanPath(this, extension, path.c_str(), path.size(), recursive);
1419 }
ReadFileToMem
std::unique_ptr< char > ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1262
MatchesExtension
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1292
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:848
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:103
_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:1122
strtolower
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
Definition: string.cpp:372
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:572
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:1370
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:1130
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:1351
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:927
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:493
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:523
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:620
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:983
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:1240
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:1309
SimplifyFileName
static void SimplifyFileName(char *name)
Simplify filenames from tars.
Definition: fileio.cpp:556
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:1003
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:369
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:385
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:961
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