OpenTTD Source  1.10.1
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 <algorithm>
28 
29 #ifdef WITH_XDG_BASEDIR
30 #include <basedir.h>
31 #endif
32 
33 #include "safeguards.h"
34 
36 #define FIO_BUFFER_SIZE 512
37 
39 struct Fio {
40  byte *buffer, *buffer_end;
41  size_t pos;
42  FILE *cur_fh;
43  const char *filename;
46  const char *filenames[MAX_FILE_SLOTS];
48 #if defined(LIMITED_FDS)
49  uint open_handles;
50  uint usage_count[MAX_FILE_SLOTS];
51 #endif /* LIMITED_FDS */
52 };
53 
54 static Fio _fio;
55 
57 static bool _do_scan_working_directory = true;
58 
59 extern char *_config_file;
60 extern char *_highscore_file;
61 
66 size_t FioGetPos()
67 {
68  return _fio.pos + (_fio.buffer - _fio.buffer_end);
69 }
70 
76 const char *FioGetFilename(uint8 slot)
77 {
78  return _fio.shortnames[slot];
79 }
80 
86 void FioSeekTo(size_t pos, int mode)
87 {
88  if (mode == SEEK_CUR) pos += FioGetPos();
89  _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
90  _fio.pos = pos;
91  if (fseek(_fio.cur_fh, _fio.pos, SEEK_SET) < 0) {
92  DEBUG(misc, 0, "Seeking in %s failed", _fio.filename);
93  }
94 }
95 
96 #if defined(LIMITED_FDS)
97 static void FioRestoreFile(int slot)
98 {
99  /* Do we still have the file open, or should we reopen it? */
100  if (_fio.handles[slot] == nullptr) {
101  DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
102  FioOpenFile(slot, _fio.filenames[slot]);
103  }
104  _fio.usage_count[slot]++;
105 }
106 #endif /* LIMITED_FDS */
107 
113 void FioSeekToFile(uint8 slot, size_t pos)
114 {
115  FILE *f;
116 #if defined(LIMITED_FDS)
117  /* Make sure we have this file open */
118  FioRestoreFile(slot);
119 #endif /* LIMITED_FDS */
120  f = _fio.handles[slot];
121  assert(f != nullptr);
122  _fio.cur_fh = f;
123  _fio.filename = _fio.filenames[slot];
124  FioSeekTo(pos, SEEK_SET);
125 }
126 
132 {
133  if (_fio.buffer == _fio.buffer_end) {
134  _fio.buffer = _fio.buffer_start;
135  size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
136  _fio.pos += size;
137  _fio.buffer_end = _fio.buffer_start + size;
138 
139  if (size == 0) return 0;
140  }
141  return *_fio.buffer++;
142 }
143 
148 void FioSkipBytes(int n)
149 {
150  for (;;) {
151  int m = min(_fio.buffer_end - _fio.buffer, n);
152  _fio.buffer += m;
153  n -= m;
154  if (n == 0) break;
155  FioReadByte();
156  n--;
157  }
158 }
159 
164 uint16 FioReadWord()
165 {
166  byte b = FioReadByte();
167  return (FioReadByte() << 8) | b;
168 }
169 
174 uint32 FioReadDword()
175 {
176  uint b = FioReadWord();
177  return (FioReadWord() << 16) | b;
178 }
179 
185 void FioReadBlock(void *ptr, size_t size)
186 {
187  FioSeekTo(FioGetPos(), SEEK_SET);
188  _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
189 }
190 
195 static inline void FioCloseFile(int slot)
196 {
197  if (_fio.handles[slot] != nullptr) {
198  fclose(_fio.handles[slot]);
199 
200  free(_fio.shortnames[slot]);
201  _fio.shortnames[slot] = nullptr;
202 
203  _fio.handles[slot] = nullptr;
204 #if defined(LIMITED_FDS)
205  _fio.open_handles--;
206 #endif /* LIMITED_FDS */
207  }
208 }
209 
212 {
213  for (int i = 0; i != lengthof(_fio.handles); i++) {
214  FioCloseFile(i);
215  }
216 }
217 
218 #if defined(LIMITED_FDS)
219 static void FioFreeHandle()
220 {
221  /* If we are about to open a file that will exceed the limit, close a file */
222  if (_fio.open_handles + 1 == LIMITED_FDS) {
223  uint i, count;
224  int slot;
225 
226  count = UINT_MAX;
227  slot = -1;
228  /* Find the file that is used the least */
229  for (i = 0; i < lengthof(_fio.handles); i++) {
230  if (_fio.handles[i] != nullptr && _fio.usage_count[i] < count) {
231  count = _fio.usage_count[i];
232  slot = i;
233  }
234  }
235  assert(slot != -1);
236  DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
237  FioCloseFile(slot);
238  }
239 }
240 #endif /* LIMITED_FDS */
241 
248 void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
249 {
250  FILE *f;
251 
252 #if defined(LIMITED_FDS)
253  FioFreeHandle();
254 #endif /* LIMITED_FDS */
255  f = FioFOpenFile(filename, "rb", subdir);
256  if (f == nullptr) usererror("Cannot open file '%s'", filename);
257  long pos = ftell(f);
258  if (pos < 0) usererror("Cannot read file '%s'", filename);
259 
260  FioCloseFile(slot); // if file was opened before, close it
261  _fio.handles[slot] = f;
262  _fio.filenames[slot] = filename;
263 
264  /* Store the filename without path and extension */
265  const char *t = strrchr(filename, PATHSEPCHAR);
266  _fio.shortnames[slot] = stredup(t == nullptr ? filename : t);
267  char *t2 = strrchr(_fio.shortnames[slot], '.');
268  if (t2 != nullptr) *t2 = '\0';
269  strtolower(_fio.shortnames[slot]);
270 
271 #if defined(LIMITED_FDS)
272  _fio.usage_count[slot] = 0;
273  _fio.open_handles++;
274 #endif /* LIMITED_FDS */
275  FioSeekToFile(slot, (uint32)pos);
276 }
277 
278 static const char * const _subdirs[] = {
279  "",
280  "save" PATHSEP,
281  "save" PATHSEP "autosave" PATHSEP,
282  "scenario" PATHSEP,
283  "scenario" PATHSEP "heightmap" PATHSEP,
284  "gm" PATHSEP,
285  "data" PATHSEP,
286  "baseset" PATHSEP,
287  "newgrf" PATHSEP,
288  "lang" PATHSEP,
289  "ai" PATHSEP,
290  "ai" PATHSEP "library" PATHSEP,
291  "game" PATHSEP,
292  "game" PATHSEP "library" PATHSEP,
293  "screenshot" PATHSEP,
294 };
295 assert_compile(lengthof(_subdirs) == NUM_SUBDIRS);
296 
297 const char *_searchpaths[NUM_SEARCHPATHS];
298 TarList _tar_list[NUM_SUBDIRS];
299 TarFileList _tar_filelist[NUM_SUBDIRS];
300 
301 typedef std::map<std::string, std::string> TarLinkList;
302 static TarLinkList _tar_linklist[NUM_SUBDIRS];
303 
310 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
311 {
312  FILE *f = FioFOpenFile(filename, "rb", subdir);
313  if (f == nullptr) return false;
314 
315  FioFCloseFile(f);
316  return true;
317 }
318 
324 bool FileExists(const char *filename)
325 {
326  return access(OTTD2FS(filename), 0) == 0;
327 }
328 
332 void FioFCloseFile(FILE *f)
333 {
334  fclose(f);
335 }
336 
337 char *FioGetFullPath(char *buf, const char *last, Searchpath sp, Subdirectory subdir, const char *filename)
338 {
339  assert(subdir < NUM_SUBDIRS);
340  assert(sp < NUM_SEARCHPATHS);
341 
342  seprintf(buf, last, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
343  return buf;
344 }
345 
354 char *FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
355 {
356  Searchpath sp;
357  assert(subdir < NUM_SUBDIRS);
358 
359  FOR_ALL_SEARCHPATHS(sp) {
360  FioGetFullPath(buf, last, sp, subdir, filename);
361  if (FileExists(buf)) return buf;
362 #if !defined(_WIN32)
363  /* Be, as opening files, aware that sometimes the filename
364  * might be in uppercase when it is in lowercase on the
365  * disk. Of course Windows doesn't care about casing. */
366  if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
367 #endif
368  }
369 
370  return nullptr;
371 }
372 
373 char *FioAppendDirectory(char *buf, const char *last, Searchpath sp, Subdirectory subdir)
374 {
375  assert(subdir < NUM_SUBDIRS);
376  assert(sp < NUM_SEARCHPATHS);
377 
378  seprintf(buf, last, "%s%s", _searchpaths[sp], _subdirs[subdir]);
379  return buf;
380 }
381 
382 char *FioGetDirectory(char *buf, const char *last, Subdirectory subdir)
383 {
384  Searchpath sp;
385 
386  /* Find and return the first valid directory */
387  FOR_ALL_SEARCHPATHS(sp) {
388  char *ret = FioAppendDirectory(buf, last, sp, subdir);
389  if (FileExists(buf)) return ret;
390  }
391 
392  /* Could not find the directory, fall back to a base path */
393  strecpy(buf, _personal_dir, last);
394 
395  return buf;
396 }
397 
398 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
399 {
400 #if defined(_WIN32) && defined(UNICODE)
401  /* fopen is implemented as a define with ellipses for
402  * Unicode support (prepend an L). As we are not sending
403  * a string, but a variable, it 'renames' the variable,
404  * so make that variable to makes it compile happily */
405  wchar_t Lmode[5];
406  MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
407 #endif
408  FILE *f = nullptr;
409  char buf[MAX_PATH];
410 
411  if (subdir == NO_DIRECTORY) {
412  strecpy(buf, filename, lastof(buf));
413  } else {
414  seprintf(buf, lastof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
415  }
416 
417 #if defined(_WIN32)
418  if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return nullptr;
419 #endif
420 
421  f = fopen(buf, mode);
422 #if !defined(_WIN32)
423  if (f == nullptr && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
424  f = fopen(buf, mode);
425  }
426 #endif
427  if (f != nullptr && filesize != nullptr) {
428  /* Find the size of the file */
429  fseek(f, 0, SEEK_END);
430  *filesize = ftell(f);
431  fseek(f, 0, SEEK_SET);
432  }
433  return f;
434 }
435 
443 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
444 {
445  FILE *f = fopen(entry->tar_filename, "rb");
446  if (f == nullptr) return f;
447 
448  if (fseek(f, entry->position, SEEK_SET) < 0) {
449  fclose(f);
450  return nullptr;
451  }
452 
453  if (filesize != nullptr) *filesize = entry->size;
454  return f;
455 }
456 
463 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
464 {
465  FILE *f = nullptr;
466  Searchpath sp;
467 
468  assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
469 
470  FOR_ALL_SEARCHPATHS(sp) {
471  f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
472  if (f != nullptr || subdir == NO_DIRECTORY) break;
473  }
474 
475  /* We can only use .tar in case of data-dir, and read-mode */
476  if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
477  static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
478  char resolved_name[MAX_RESOLVED_LENGTH];
479 
480  /* Filenames in tars are always forced to be lowercase */
481  strecpy(resolved_name, filename, lastof(resolved_name));
482  strtolower(resolved_name);
483 
484  size_t resolved_len = strlen(resolved_name);
485 
486  /* Resolve ONE directory link */
487  for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
488  const std::string &src = link->first;
489  size_t len = src.length();
490  if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
491  /* Apply link */
492  char resolved_name2[MAX_RESOLVED_LENGTH];
493  const std::string &dest = link->second;
494  strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
495  strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
496  strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
497  break; // Only resolve one level
498  }
499  }
500 
501  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
502  if (it != _tar_filelist[subdir].end()) {
503  f = FioFOpenFileTar(&((*it).second), filesize);
504  }
505  }
506 
507  /* Sometimes a full path is given. To support
508  * the 'subdirectory' must be 'removed'. */
509  if (f == nullptr && subdir != NO_DIRECTORY) {
510  switch (subdir) {
511  case BASESET_DIR:
512  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
513  if (f != nullptr) break;
514  FALLTHROUGH;
515  case NEWGRF_DIR:
516  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
517  break;
518 
519  default:
520  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
521  break;
522  }
523  }
524 
525  return f;
526 }
527 
533 void FioCreateDirectory(const char *name)
534 {
535  char dirname[MAX_PATH];
536  strecpy(dirname, name, lastof(dirname));
537  char *p = strrchr(dirname, PATHSEPCHAR);
538  if (p != nullptr) {
539  *p = '\0';
540  DIR *dir = ttd_opendir(dirname);
541  if (dir == nullptr) {
542  FioCreateDirectory(dirname); // Try creating the parent directory, if we couldn't open it
543  } else {
544  closedir(dir);
545  }
546  }
547 
548  /* Ignore directory creation errors; they'll surface later on, and most
549  * of the time they are 'directory already exists' errors anyhow. */
550 #if defined(_WIN32)
551  CreateDirectory(OTTD2FS(name), nullptr);
552 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
553  mkdir(OTTD2FS(name));
554 #else
555  mkdir(OTTD2FS(name), 0755);
556 #endif
557 }
558 
566 bool AppendPathSeparator(char *buf, const char *last)
567 {
568  size_t s = strlen(buf);
569 
570  /* Length of string + path separator + '\0' */
571  if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
572  if (&buf[s] >= last) return false;
573 
574  seprintf(buf + s, last, "%c", PATHSEPCHAR);
575  }
576 
577  return true;
578 }
579 
580 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
581 {
582  std::string src = srcParam;
583  std::string dest = destParam;
584  /* Tar internals assume lowercase */
585  std::transform(src.begin(), src.end(), src.begin(), tolower);
586  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
587 
588  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
589  if (dest_file != _tar_filelist[subdir].end()) {
590  /* Link to file. Process the link like the destination file. */
591  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
592  } else {
593  /* Destination file not found. Assume 'link to directory'
594  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
595  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
596  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
597  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
598  }
599 }
600 
606 static void SimplifyFileName(char *name)
607 {
608  /* Force lowercase */
609  strtolower(name);
610 
611  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
612 #if (PATHSEPCHAR != '/')
613  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
614 #endif
615 }
616 
623 {
624  _tar_filelist[sd].clear();
625  _tar_list[sd].clear();
626  uint num = this->Scan(".tar", sd, false);
627  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
628  return num;
629 }
630 
631 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
632 {
633  DEBUG(misc, 1, "Scanning for tars");
634  TarScanner fs;
635  uint num = 0;
636  if (mode & TarScanner::BASESET) {
637  num += fs.DoScan(BASESET_DIR);
638  }
639  if (mode & TarScanner::NEWGRF) {
640  num += fs.DoScan(NEWGRF_DIR);
641  }
642  if (mode & TarScanner::AI) {
643  num += fs.DoScan(AI_DIR);
644  num += fs.DoScan(AI_LIBRARY_DIR);
645  }
646  if (mode & TarScanner::GAME) {
647  num += fs.DoScan(GAME_DIR);
648  num += fs.DoScan(GAME_LIBRARY_DIR);
649  }
650  if (mode & TarScanner::SCENARIO) {
651  num += fs.DoScan(SCENARIO_DIR);
652  num += fs.DoScan(HEIGHTMAP_DIR);
653  }
654  DEBUG(misc, 1, "Scan complete, found %d files", num);
655  return num;
656 }
657 
665 {
666  this->subdir = sd;
667  return this->AddFile(filename, 0);
668 }
669 
670 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
671 {
672  /* No tar within tar. */
673  assert(tar_filename == nullptr);
674 
675  /* The TAR-header, repeated for every file */
676  struct TarHeader {
677  char name[100];
678  char mode[8];
679  char uid[8];
680  char gid[8];
681  char size[12];
682  char mtime[12];
683  char chksum[8];
684  char typeflag;
685  char linkname[100];
686  char magic[6];
687  char version[2];
688  char uname[32];
689  char gname[32];
690  char devmajor[8];
691  char devminor[8];
692  char prefix[155];
693 
694  char unused[12];
695  };
696 
697  /* Check if we already seen this file */
698  TarList::iterator it = _tar_list[this->subdir].find(filename);
699  if (it != _tar_list[this->subdir].end()) return false;
700 
701  FILE *f = fopen(filename, "rb");
702  /* Although the file has been found there can be
703  * a number of reasons we cannot open the file.
704  * Most common case is when we simply have not
705  * been given read access. */
706  if (f == nullptr) return false;
707 
708  const char *dupped_filename = stredup(filename);
709  _tar_list[this->subdir][filename].filename = dupped_filename;
710  _tar_list[this->subdir][filename].dirname = nullptr;
711 
712  TarLinkList links;
713 
714  TarHeader th;
715  char buf[sizeof(th.name) + 1], *end;
716  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
717  char link[sizeof(th.linkname) + 1];
718  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
719  size_t num = 0, pos = 0;
720 
721  /* Make a char of 512 empty bytes */
722  char empty[512];
723  memset(&empty[0], 0, sizeof(empty));
724 
725  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
726  size_t num_bytes_read = fread(&th, 1, 512, f);
727  if (num_bytes_read != 512) break;
728  pos += num_bytes_read;
729 
730  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
731  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
732  /* If we have only zeros in the block, it can be an end-of-file indicator */
733  if (memcmp(&th, &empty[0], 512) == 0) continue;
734 
735  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
736  fclose(f);
737  return false;
738  }
739 
740  name[0] = '\0';
741 
742  /* The prefix contains the directory-name */
743  if (th.prefix[0] != '\0') {
744  strecpy(name, th.prefix, lastof(name));
745  strecat(name, PATHSEP, lastof(name));
746  }
747 
748  /* Copy the name of the file in a safe way at the end of 'name' */
749  strecat(name, th.name, lastof(name));
750 
751  /* Calculate the size of the file.. for some strange reason this is stored as a string */
752  strecpy(buf, th.size, lastof(buf));
753  size_t skip = strtoul(buf, &end, 8);
754 
755  switch (th.typeflag) {
756  case '\0':
757  case '0': { // regular file
758  /* Ignore empty files */
759  if (skip == 0) break;
760 
761  if (strlen(name) == 0) break;
762 
763  /* Store this entry in the list */
764  TarFileListEntry entry;
765  entry.tar_filename = dupped_filename;
766  entry.size = skip;
767  entry.position = pos;
768 
769  /* Convert to lowercase and our PATHSEPCHAR */
770  SimplifyFileName(name);
771 
772  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
773  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
774 
775  break;
776  }
777 
778  case '1': // hard links
779  case '2': { // symbolic links
780  /* Copy the destination of the link in a safe way at the end of 'linkname' */
781  strecpy(link, th.linkname, lastof(link));
782 
783  if (strlen(name) == 0 || strlen(link) == 0) break;
784 
785  /* Convert to lowercase and our PATHSEPCHAR */
786  SimplifyFileName(name);
787  SimplifyFileName(link);
788 
789  /* Only allow relative links */
790  if (link[0] == PATHSEPCHAR) {
791  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
792  break;
793  }
794 
795  /* Process relative path.
796  * Note: The destination of links must not contain any directory-links. */
797  strecpy(dest, name, lastof(dest));
798  char *destpos = strrchr(dest, PATHSEPCHAR);
799  if (destpos == nullptr) destpos = dest;
800  *destpos = '\0';
801 
802  char *pos = link;
803  while (*pos != '\0') {
804  char *next = strchr(pos, PATHSEPCHAR);
805  if (next == nullptr) {
806  next = pos + strlen(pos);
807  } else {
808  /* Terminate the substring up to the path separator character. */
809  *next++= '\0';
810  }
811 
812  if (strcmp(pos, ".") == 0) {
813  /* Skip '.' (current dir) */
814  } else if (strcmp(pos, "..") == 0) {
815  /* level up */
816  if (dest[0] == '\0') {
817  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
818  break;
819  }
820 
821  /* Truncate 'dest' after last PATHSEPCHAR.
822  * This assumes that the truncated part is a real directory and not a link. */
823  destpos = strrchr(dest, PATHSEPCHAR);
824  if (destpos == nullptr) destpos = dest;
825  *destpos = '\0';
826  } else {
827  /* Append at end of 'dest' */
828  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
829  destpos = strecpy(destpos, pos, lastof(dest));
830  }
831 
832  if (destpos >= lastof(dest)) {
833  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename);
834  fclose(f);
835  return false;
836  }
837 
838  pos = next;
839  }
840 
841  /* Store links in temporary list */
842  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
843  links.insert(TarLinkList::value_type(name, dest));
844 
845  break;
846  }
847 
848  case '5': // directory
849  /* Convert to lowercase and our PATHSEPCHAR */
850  SimplifyFileName(name);
851 
852  /* Store the first directory name we detect */
853  DEBUG(misc, 6, "Found dir in tar: %s", name);
854  if (_tar_list[this->subdir][filename].dirname == nullptr) _tar_list[this->subdir][filename].dirname = stredup(name);
855  break;
856 
857  default:
858  /* Ignore other types */
859  break;
860  }
861 
862  /* Skip to the next block.. */
863  skip = Align(skip, 512);
864  if (fseek(f, skip, SEEK_CUR) < 0) {
865  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
866  fclose(f);
867  return false;
868  }
869  pos += skip;
870  }
871 
872  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
873  fclose(f);
874 
875  /* Resolve file links and store directory links.
876  * We restrict usage of links to two cases:
877  * 1) Links to directories:
878  * Both the source path and the destination path must NOT contain any further links.
879  * When resolving files at most one directory link is resolved.
880  * 2) Links to files:
881  * The destination path must NOT contain any links.
882  * The source path may contain one directory link.
883  */
884  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
885  const std::string &src = link->first;
886  const std::string &dest = link->second;
887  TarAddLink(src, dest, this->subdir);
888  }
889 
890  return true;
891 }
892 
900 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
901 {
902  TarList::iterator it = _tar_list[subdir].find(tar_filename);
903  /* We don't know the file. */
904  if (it == _tar_list[subdir].end()) return false;
905 
906  const char *dirname = (*it).second.dirname;
907 
908  /* The file doesn't have a sub directory! */
909  if (dirname == nullptr) return false;
910 
911  char filename[MAX_PATH];
912  strecpy(filename, tar_filename, lastof(filename));
913  char *p = strrchr(filename, PATHSEPCHAR);
914  /* The file's path does not have a separator? */
915  if (p == nullptr) return false;
916 
917  p++;
918  strecpy(p, dirname, lastof(filename));
919  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
920  FioCreateDirectory(filename);
921 
922  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
923  if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
924 
925  strecpy(p, (*it2).first.c_str(), lastof(filename));
926 
927  DEBUG(misc, 9, " extracting %s", filename);
928 
929  /* First open the file in the .tar. */
930  size_t to_copy = 0;
931  FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
932  if (in == nullptr) {
933  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
934  return false;
935  }
936 
937  /* Now open the 'output' file. */
938  FILE *out = fopen(filename, "wb");
939  if (out == nullptr) {
940  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
941  fclose(in);
942  return false;
943  }
944 
945  /* Now read from the tar and write it into the file. */
946  char buffer[4096];
947  size_t read;
948  for (; to_copy != 0; to_copy -= read) {
949  read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
950  if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
951  }
952 
953  /* Close everything up. */
954  fclose(in);
955  fclose(out);
956 
957  if (to_copy != 0) {
958  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
959  return false;
960  }
961  }
962 
963  DEBUG(misc, 9, " extraction successful");
964  return true;
965 }
966 
967 #if defined(_WIN32)
968 
973 extern void DetermineBasePaths(const char *exe);
974 #else /* defined(_WIN32) */
975 
983 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
984 {
985  char tmp[MAX_PATH];
986  strecpy(tmp, exe, lastof(tmp));
987 
988  bool success = false;
989 #ifdef WITH_COCOA
990  char *app_bundle = strchr(tmp, '.');
991  while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
992 
993  if (app_bundle != nullptr) *app_bundle = '\0';
994 #endif /* WITH_COCOA */
995  char *s = strrchr(tmp, PATHSEPCHAR);
996  if (s != nullptr) {
997  *s = '\0';
998  if (chdir(tmp) != 0) {
999  DEBUG(misc, 0, "Directory with the binary does not exist?");
1000  } else {
1001  success = true;
1002  }
1003  }
1004  return success;
1005 }
1006 
1018 {
1019  /* No working directory, so nothing to do. */
1020  if (_searchpaths[SP_WORKING_DIR] == nullptr) return false;
1021 
1022  /* Working directory is root, so do nothing. */
1023  if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
1024 
1025  /* No personal/home directory, so the working directory won't be that. */
1026  if (_searchpaths[SP_PERSONAL_DIR] == nullptr) return true;
1027 
1028  char tmp[MAX_PATH];
1029  seprintf(tmp, lastof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
1030  AppendPathSeparator(tmp, lastof(tmp));
1031  return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
1032 }
1033 
1038 void DetermineBasePaths(const char *exe)
1039 {
1040  char tmp[MAX_PATH];
1041 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1042  const char *xdg_data_home = xdgDataHome(nullptr);
1043  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", xdg_data_home,
1044  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1045  free(xdg_data_home);
1046 
1047  AppendPathSeparator(tmp, lastof(tmp));
1048  _searchpaths[SP_PERSONAL_DIR_XDG] = stredup(tmp);
1049 #endif
1050 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1051  _searchpaths[SP_PERSONAL_DIR] = nullptr;
1052 #else
1053 #ifdef __HAIKU__
1054  BPath path;
1055  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1056  const char *homedir = stredup(path.Path());
1057 #else
1058  /* getenv is highly unsafe; duplicate it as soon as possible,
1059  * or at least before something else touches the environment
1060  * variables in any way. It can also contain all kinds of
1061  * unvalidated data we rather not want internally. */
1062  const char *homedir = getenv("HOME");
1063  if (homedir != nullptr) {
1064  homedir = stredup(homedir);
1065  }
1066 
1067  if (homedir == nullptr) {
1068  const struct passwd *pw = getpwuid(getuid());
1069  homedir = (pw == nullptr) ? nullptr : stredup(pw->pw_dir);
1070  }
1071 #endif
1072 
1073  if (homedir != nullptr) {
1074  ValidateString(homedir);
1075  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
1076  AppendPathSeparator(tmp, lastof(tmp));
1077 
1079  free(homedir);
1080  } else {
1081  _searchpaths[SP_PERSONAL_DIR] = nullptr;
1082  }
1083 #endif
1084 
1085 #if defined(WITH_SHARED_DIR)
1086  seprintf(tmp, lastof(tmp), "%s", SHARED_DIR);
1087  AppendPathSeparator(tmp, lastof(tmp));
1089 #else
1090  _searchpaths[SP_SHARED_DIR] = nullptr;
1091 #endif
1092 
1093  if (getcwd(tmp, MAX_PATH) == nullptr) *tmp = '\0';
1094  AppendPathSeparator(tmp, lastof(tmp));
1096 
1098 
1099  /* Change the working directory to that one of the executable */
1101  if (getcwd(tmp, MAX_PATH) == nullptr) *tmp = '\0';
1102  AppendPathSeparator(tmp, lastof(tmp));
1104  } else {
1105  _searchpaths[SP_BINARY_DIR] = nullptr;
1106  }
1107 
1108  if (_searchpaths[SP_WORKING_DIR] != nullptr) {
1109  /* Go back to the current working directory. */
1110  if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
1111  DEBUG(misc, 0, "Failed to return to working directory!");
1112  }
1113  }
1114 
1115 #if !defined(GLOBAL_DATA_DIR)
1116  _searchpaths[SP_INSTALLATION_DIR] = nullptr;
1117 #else
1118  seprintf(tmp, lastof(tmp), "%s", GLOBAL_DATA_DIR);
1119  AppendPathSeparator(tmp, lastof(tmp));
1121 #endif
1122 #ifdef WITH_COCOA
1123 extern void cocoaSetApplicationBundleDir();
1124  cocoaSetApplicationBundleDir();
1125 #else
1127 #endif
1128 }
1129 #endif /* defined(_WIN32) */
1130 
1131 const char *_personal_dir;
1132 
1139 void DeterminePaths(const char *exe)
1140 {
1141  DetermineBasePaths(exe);
1142 
1143 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1144  char config_home[MAX_PATH];
1145 
1146  const char *xdg_config_home = xdgConfigHome(nullptr);
1147  seprintf(config_home, lastof(config_home), "%s" PATHSEP "%s", xdg_config_home,
1148  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1149  free(xdg_config_home);
1150 
1151  AppendPathSeparator(config_home, lastof(config_home));
1152 #endif
1153 
1154  Searchpath sp;
1155  FOR_ALL_SEARCHPATHS(sp) {
1156  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1157  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
1158  }
1159 
1160  char *config_dir;
1161  if (_config_file != nullptr) {
1162  config_dir = stredup(_config_file);
1163  char *end = strrchr(config_dir, PATHSEPCHAR);
1164  if (end == nullptr) {
1165  config_dir[0] = '\0';
1166  } else {
1167  end[1] = '\0';
1168  }
1169  } else {
1170  char personal_dir[MAX_PATH];
1171  if (FioFindFullPath(personal_dir, lastof(personal_dir), BASE_DIR, "openttd.cfg") != nullptr) {
1172  char *end = strrchr(personal_dir, PATHSEPCHAR);
1173  if (end != nullptr) end[1] = '\0';
1174  config_dir = stredup(personal_dir);
1175  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1176  } else {
1177 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1178  /* No previous configuration file found. Use the configuration folder from XDG. */
1179  config_dir = config_home;
1180 #else
1181  static const Searchpath new_openttd_cfg_order[] = {
1183  };
1184 
1185  config_dir = nullptr;
1186  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1187  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1188  config_dir = stredup(_searchpaths[new_openttd_cfg_order[i]]);
1189  break;
1190  }
1191  }
1192  assert(config_dir != nullptr);
1193 #endif
1194  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1195  }
1196  }
1197 
1198  DEBUG(misc, 3, "%s found as config directory", config_dir);
1199 
1200  _highscore_file = str_fmt("%shs.dat", config_dir);
1201  extern char *_hotkeys_file;
1202  _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
1203  extern char *_windows_file;
1204  _windows_file = str_fmt("%swindows.cfg", config_dir);
1205 
1206 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1207  if (config_dir == config_home) {
1208  /* We are using the XDG configuration home for the config file,
1209  * then store the rest in the XDG data home folder. */
1210  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1212  } else
1213 #endif
1214  {
1215  _personal_dir = config_dir;
1216  }
1217 
1218  /* Make the necessary folders */
1219 #if defined(WITH_PERSONAL_DIR)
1220  FioCreateDirectory(config_dir);
1221  if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
1222 #endif
1223 
1224  DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
1225 
1226  static const Subdirectory default_subdirs[] = {
1228  };
1229 
1230  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1231  char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
1232  FioCreateDirectory(dir);
1233  free(dir);
1234  }
1235 
1236  /* If we have network we make a directory for the autodownloading of content */
1237  _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
1239 
1240  /* Create the directory for each of the types of content */
1242  for (uint i = 0; i < lengthof(dirs); i++) {
1243  char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
1244  FioCreateDirectory(tmp);
1245  free(tmp);
1246  }
1247 
1248  extern char *_log_file;
1249  _log_file = str_fmt("%sopenttd.log", _personal_dir);
1250 }
1251 
1257 {
1258  for (; *filename != '\0'; filename++) {
1259  switch (*filename) {
1260  /* The following characters are not allowed in filenames
1261  * on at least one of the supported operating systems: */
1262  case ':': case '\\': case '*': case '?': case '/':
1263  case '<': case '>': case '|': case '"':
1264  *filename = '_';
1265  break;
1266  }
1267  }
1268 }
1269 
1278 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
1279 {
1280  FILE *in = fopen(filename, "rb");
1281  if (in == nullptr) return nullptr;
1282 
1283  fseek(in, 0, SEEK_END);
1284  size_t len = ftell(in);
1285  fseek(in, 0, SEEK_SET);
1286  if (len > maxsize) {
1287  fclose(in);
1288  return nullptr;
1289  }
1290  byte *mem = MallocT<byte>(len + 1);
1291  mem[len] = 0;
1292  if (fread(mem, len, 1, in) != 1) {
1293  fclose(in);
1294  free(mem);
1295  return nullptr;
1296  }
1297  fclose(in);
1298 
1299  *lenp = len;
1300  return mem;
1301 }
1302 
1309 static bool MatchesExtension(const char *extension, const char *filename)
1310 {
1311  if (extension == nullptr) return true;
1312 
1313  const char *ext = strrchr(filename, extension[0]);
1314  return ext != nullptr && strcasecmp(ext, extension) == 0;
1315 }
1316 
1326 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1327 {
1328  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1329 
1330  uint num = 0;
1331  struct stat sb;
1332  struct dirent *dirent;
1333  DIR *dir;
1334 
1335  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1336 
1337  while ((dirent = readdir(dir)) != nullptr) {
1338  const char *d_name = FS2OTTD(dirent->d_name);
1339  char filename[MAX_PATH];
1340 
1341  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1342 
1343  seprintf(filename, lastof(filename), "%s%s", path, d_name);
1344 
1345  if (S_ISDIR(sb.st_mode)) {
1346  /* Directory */
1347  if (!recursive) continue;
1348  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1349  if (!AppendPathSeparator(filename, lastof(filename))) continue;
1350  num += ScanPath(fs, extension, filename, basepath_length, recursive);
1351  } else if (S_ISREG(sb.st_mode)) {
1352  /* File */
1353  if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, nullptr)) num++;
1354  }
1355  }
1356 
1357  closedir(dir);
1358 
1359  return num;
1360 }
1361 
1368 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1369 {
1370  uint num = 0;
1371  const char *filename = (*tar).first.c_str();
1372 
1373  if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1374 
1375  return num;
1376 }
1377 
1387 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1388 {
1389  this->subdir = sd;
1390 
1391  Searchpath sp;
1392  char path[MAX_PATH];
1393  TarFileList::iterator tar;
1394  uint num = 0;
1395 
1396  FOR_ALL_SEARCHPATHS(sp) {
1397  /* Don't search in the working directory */
1398  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1399 
1400  FioAppendDirectory(path, lastof(path), sp, sd);
1401  num += ScanPath(this, extension, path, strlen(path), recursive);
1402  }
1403 
1404  if (tars && sd != NO_DIRECTORY) {
1405  FOR_ALL_TARS(tar, sd) {
1406  num += ScanTar(this, extension, tar);
1407  }
1408  }
1409 
1410  switch (sd) {
1411  case BASESET_DIR:
1412  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1413  FALLTHROUGH;
1414  case NEWGRF_DIR:
1415  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1416  break;
1417 
1418  default: break;
1419  }
1420 
1421  return num;
1422 }
1423 
1432 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1433 {
1434  char path[MAX_PATH];
1435  strecpy(path, directory, lastof(path));
1436  if (!AppendPathSeparator(path, lastof(path))) return 0;
1437  return ScanPath(this, extension, path, strlen(path), recursive);
1438 }
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:1017
FILE * handles[MAX_FILE_SLOTS]
array of file handles we can have open
Definition: fileio.cpp:44
Old subdirectory for the music.
Definition: fileio_type.h:114
char *CDECL str_fmt(const char *str,...)
Format, "printf", into a newly allocated string.
Definition: string.cpp:149
FILE * cur_fh
current file handle
Definition: fileio.cpp:42
Scan for base sets.
Definition: fileio_func.h:98
static bool _do_scan_working_directory
Whether the working directory should be scanned.
Definition: fileio.cpp:57
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: depend.cpp:97
const char * FS2OTTD(const TCHAR *name)
Convert to OpenTTD&#39;s encoding from that of the local environment.
Definition: win32.cpp:558
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:108
Number of subdirectories.
Definition: fileio_type.h:124
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:332
Structs, typedefs and macros used for TAR file handling.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:407
Search in the directory where the binary resides.
Definition: fileio_type.h:139
Functions related to debugging.
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:1387
uint16 FioReadWord()
Read a word (16 bits) from the file (in low endian format).
Definition: fileio.cpp:164
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
Definition: fileio_func.h:47
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:113
Subdirectory for all screenshots.
Definition: fileio_type.h:123
#define FIO_BUFFER_SIZE
Size of the Fio data buffer.
Definition: fileio.cpp:36
void FioCloseAll()
Close all slotted open files.
Definition: fileio.cpp:211
Maximum number of slots.
Definition: fios.h:99
byte buffer_start[FIO_BUFFER_SIZE]
local buffer when read from file
Definition: fileio.cpp:45
Subdirectory for all game scripts.
Definition: fileio_type.h:121
Functions for Standard In/Out file operations.
Helper for scanning for files with tar as extension.
Definition: fileio_func.h:92
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:1368
const char * FioGetFilename(uint8 slot)
Get the filename associated with a slot.
Definition: fileio.cpp:76
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:48
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
Definition: string.cpp:330
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:131
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:116
byte FioReadByte()
Read a byte from the file.
Definition: fileio.cpp:131
const char * filename
current filename
Definition: fileio.cpp:43
virtual bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)=0
Add a file with the given filename.
Helper for scanning for files with a given name.
Definition: fileio_func.h:70
char * _log_file
File to reroute output of a forked OpenTTD to.
Definition: dedicated.cpp:12
Functions related to low-level strings.
Base directory for all scenarios.
Definition: fileio_type.h:112
byte * buffer_end
position pointer in local buffer and last valid byte of buffer
Definition: fileio.cpp:40
bool AppendPathSeparator(char *buf, const char *last)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:566
Scan for game scripts.
Definition: fileio_func.h:102
Definition: win32.cpp:92
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:95
char * _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:83
char * FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:354
static TarLinkList _tar_linklist[NUM_SUBDIRS]
List of directory links.
Definition: fileio.cpp:302
FILE * FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:463
A path without any base directory.
Definition: fileio_type.h:125
bool FileExists(const char *filename)
Test whether the given filename exists.
Definition: fileio.cpp:324
Definition of base types and functions in a cross-platform compatible way.
static bool ChangeWorkingDirectoryToExecutable(const char *exe)
Changes the working directory to the path of the give executable.
Definition: fileio.cpp:983
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:92
A number of safeguards to prevent using unsafe methods.
Scan for non-base sets.
Definition: fileio_func.h:99
void FioSeekTo(size_t pos, int mode)
Seek in the current file.
Definition: fileio.cpp:86
static void SimplifyFileName(char *name)
Simplify filenames from tars.
Definition: fileio.cpp:606
Scan for scenarios and heightmaps.
Definition: fileio_func.h:101
Base directory for all savegames.
Definition: fileio_type.h:110
Subdirectory of save for autosaves.
Definition: fileio_type.h:111
void SanitizeFilename(char *filename)
Sanitizes a filename, i.e.
Definition: fileio.cpp:1256
Base directory for all subdirectories.
Definition: fileio_type.h:109
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:136
const char * _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:1131
void FioSeekToFile(uint8 slot, size_t pos)
Switch to a different file and seek to a position.
Definition: fileio.cpp:113
Subdirectory for all GS libraries.
Definition: fileio_type.h:122
Search within the autodownload directory.
Definition: fileio_type.h:142
void FioReadBlock(void *ptr, size_t size)
Read a block.
Definition: fileio.cpp:185
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:40
bool FioCheckFileExists(const char *filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:310
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
Definition: fileio.cpp:1038
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:576
Subdirectory for all NewGRFs.
Definition: fileio_type.h:117
Structure for keeping several open files with just one data buffer.
Definition: fileio.cpp:39
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:1326
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:622
const char * filenames[MAX_FILE_SLOTS]
array of filenames we (should) have open
Definition: fileio.cpp:46
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename=nullptr) override
Add a file with the given filename.
Definition: fileio.cpp:670
void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
Open a slotted file.
Definition: fileio.cpp:248
Subdirectory for all AI libraries.
Definition: fileio_type.h:120
uint32 FioReadDword()
Read a double word (32 bits) from the file (in low endian format).
Definition: fileio.cpp:174
Mode
The mode of tar scanning.
Definition: fileio_func.h:96
Search in the personal directory.
Definition: fileio_type.h:137
char * shortnames[MAX_FILE_SLOTS]
array of short names for spriteloader&#39;s use
Definition: fileio.cpp:47
void DeterminePaths(const char *exe)
Acquire the base paths (personal dir and game data dir), fill all other paths (save dir...
Definition: fileio.cpp:1139
char * _highscore_file
The file to store the highscore data in.
Definition: highscore.cpp:23
Declarations for savegames operations.
static bool MatchesExtension(const char *extension, const char *filename)
Helper to see whether a given filename matches the extension.
Definition: fileio.cpp:1309
const char * _searchpaths[NUM_SEARCHPATHS]
The search paths OpenTTD could search through.
Definition: fileio.cpp:297
void FioSkipBytes(int n)
Skip n bytes ahead in the file.
Definition: fileio.cpp:148
Subdirectory for all AI files.
Definition: fileio_type.h:119
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:66
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:144
Search in the working directory.
Definition: fileio_type.h:133
FILE * FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
Opens a file from inside a tar archive.
Definition: fileio.cpp:443
Scan for AIs and its libraries.
Definition: fileio_func.h:100
Search in the installation directory.
Definition: fileio_type.h:140
bool ExtractTar(const char *tar_filename, Subdirectory subdir)
Extract the tar with the given filename in the directory where the tar resides.
Definition: fileio.cpp:900
Search within the application bundle.
Definition: fileio_type.h:141
static Fio _fio
Fio instance.
Definition: fileio.cpp:54
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
size_t pos
current (system) position in file
Definition: fileio.cpp:41
void * ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
Load a file into memory.
Definition: fileio.cpp:1278
void ValidateString(const char *str)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
Definition: string.cpp:243
static bool IsValidSearchPath(Searchpath sp)
Checks whether the given search path is a valid search path.
Definition: fileio_func.h:41
size_t FioGetPos()
Get position in the current file.
Definition: fileio.cpp:66
void FioCreateDirectory(const char *name)
Create a directory with the given name If the parent directory does not exist, it will try to create ...
Definition: fileio.cpp:533
static void FioCloseFile(int slot)
Close the file at the given slot number.
Definition: fileio.cpp:195
Search in the shared directory, like &#39;Shared Files&#39; under Windows.
Definition: fileio_type.h:138
char * _windows_file
Config file to store WindowDesc.
Definition: window.cpp:89
Old subdirectory for the data.
Definition: fileio_type.h:115