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