OpenTTD
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 
532 void FioCreateDirectory(const char *name)
533 {
534  /* Ignore directory creation errors; they'll surface later on, and most
535  * of the time they are 'directory already exists' errors anyhow. */
536 #if defined(_WIN32)
537  CreateDirectory(OTTD2FS(name), nullptr);
538 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
539  mkdir(OTTD2FS(name));
540 #else
541  mkdir(OTTD2FS(name), 0755);
542 #endif
543 }
544 
552 bool AppendPathSeparator(char *buf, const char *last)
553 {
554  size_t s = strlen(buf);
555 
556  /* Length of string + path separator + '\0' */
557  if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
558  if (&buf[s] >= last) return false;
559 
560  seprintf(buf + s, last, "%c", PATHSEPCHAR);
561  }
562 
563  return true;
564 }
565 
566 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
567 {
568  std::string src = srcParam;
569  std::string dest = destParam;
570  /* Tar internals assume lowercase */
571  std::transform(src.begin(), src.end(), src.begin(), tolower);
572  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
573 
574  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
575  if (dest_file != _tar_filelist[subdir].end()) {
576  /* Link to file. Process the link like the destination file. */
577  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
578  } else {
579  /* Destination file not found. Assume 'link to directory'
580  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
581  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
582  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
583  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
584  }
585 }
586 
592 static void SimplifyFileName(char *name)
593 {
594  /* Force lowercase */
595  strtolower(name);
596 
597  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
598 #if (PATHSEPCHAR != '/')
599  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
600 #endif
601 }
602 
609 {
610  _tar_filelist[sd].clear();
611  _tar_list[sd].clear();
612  uint num = this->Scan(".tar", sd, false);
613  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
614  return num;
615 }
616 
617 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
618 {
619  DEBUG(misc, 1, "Scanning for tars");
620  TarScanner fs;
621  uint num = 0;
622  if (mode & TarScanner::BASESET) {
623  num += fs.DoScan(BASESET_DIR);
624  }
625  if (mode & TarScanner::NEWGRF) {
626  num += fs.DoScan(NEWGRF_DIR);
627  }
628  if (mode & TarScanner::AI) {
629  num += fs.DoScan(AI_DIR);
630  num += fs.DoScan(AI_LIBRARY_DIR);
631  }
632  if (mode & TarScanner::GAME) {
633  num += fs.DoScan(GAME_DIR);
634  num += fs.DoScan(GAME_LIBRARY_DIR);
635  }
636  if (mode & TarScanner::SCENARIO) {
637  num += fs.DoScan(SCENARIO_DIR);
638  num += fs.DoScan(HEIGHTMAP_DIR);
639  }
640  DEBUG(misc, 1, "Scan complete, found %d files", num);
641  return num;
642 }
643 
651 {
652  this->subdir = sd;
653  return this->AddFile(filename, 0);
654 }
655 
656 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
657 {
658  /* No tar within tar. */
659  assert(tar_filename == nullptr);
660 
661  /* The TAR-header, repeated for every file */
662  struct TarHeader {
663  char name[100];
664  char mode[8];
665  char uid[8];
666  char gid[8];
667  char size[12];
668  char mtime[12];
669  char chksum[8];
670  char typeflag;
671  char linkname[100];
672  char magic[6];
673  char version[2];
674  char uname[32];
675  char gname[32];
676  char devmajor[8];
677  char devminor[8];
678  char prefix[155];
679 
680  char unused[12];
681  };
682 
683  /* Check if we already seen this file */
684  TarList::iterator it = _tar_list[this->subdir].find(filename);
685  if (it != _tar_list[this->subdir].end()) return false;
686 
687  FILE *f = fopen(filename, "rb");
688  /* Although the file has been found there can be
689  * a number of reasons we cannot open the file.
690  * Most common case is when we simply have not
691  * been given read access. */
692  if (f == nullptr) return false;
693 
694  const char *dupped_filename = stredup(filename);
695  _tar_list[this->subdir][filename].filename = dupped_filename;
696  _tar_list[this->subdir][filename].dirname = nullptr;
697 
698  TarLinkList links;
699 
700  TarHeader th;
701  char buf[sizeof(th.name) + 1], *end;
702  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
703  char link[sizeof(th.linkname) + 1];
704  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
705  size_t num = 0, pos = 0;
706 
707  /* Make a char of 512 empty bytes */
708  char empty[512];
709  memset(&empty[0], 0, sizeof(empty));
710 
711  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
712  size_t num_bytes_read = fread(&th, 1, 512, f);
713  if (num_bytes_read != 512) break;
714  pos += num_bytes_read;
715 
716  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
717  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
718  /* If we have only zeros in the block, it can be an end-of-file indicator */
719  if (memcmp(&th, &empty[0], 512) == 0) continue;
720 
721  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
722  fclose(f);
723  return false;
724  }
725 
726  name[0] = '\0';
727 
728  /* The prefix contains the directory-name */
729  if (th.prefix[0] != '\0') {
730  strecpy(name, th.prefix, lastof(name));
731  strecat(name, PATHSEP, lastof(name));
732  }
733 
734  /* Copy the name of the file in a safe way at the end of 'name' */
735  strecat(name, th.name, lastof(name));
736 
737  /* Calculate the size of the file.. for some strange reason this is stored as a string */
738  strecpy(buf, th.size, lastof(buf));
739  size_t skip = strtoul(buf, &end, 8);
740 
741  switch (th.typeflag) {
742  case '\0':
743  case '0': { // regular file
744  /* Ignore empty files */
745  if (skip == 0) break;
746 
747  if (strlen(name) == 0) break;
748 
749  /* Store this entry in the list */
750  TarFileListEntry entry;
751  entry.tar_filename = dupped_filename;
752  entry.size = skip;
753  entry.position = pos;
754 
755  /* Convert to lowercase and our PATHSEPCHAR */
756  SimplifyFileName(name);
757 
758  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
759  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
760 
761  break;
762  }
763 
764  case '1': // hard links
765  case '2': { // symbolic links
766  /* Copy the destination of the link in a safe way at the end of 'linkname' */
767  strecpy(link, th.linkname, lastof(link));
768 
769  if (strlen(name) == 0 || strlen(link) == 0) break;
770 
771  /* Convert to lowercase and our PATHSEPCHAR */
772  SimplifyFileName(name);
773  SimplifyFileName(link);
774 
775  /* Only allow relative links */
776  if (link[0] == PATHSEPCHAR) {
777  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
778  break;
779  }
780 
781  /* Process relative path.
782  * Note: The destination of links must not contain any directory-links. */
783  strecpy(dest, name, lastof(dest));
784  char *destpos = strrchr(dest, PATHSEPCHAR);
785  if (destpos == nullptr) destpos = dest;
786  *destpos = '\0';
787 
788  char *pos = link;
789  while (*pos != '\0') {
790  char *next = strchr(pos, PATHSEPCHAR);
791  if (next == nullptr) {
792  next = pos + strlen(pos);
793  } else {
794  /* Terminate the substring up to the path separator character. */
795  *next++= '\0';
796  }
797 
798  if (strcmp(pos, ".") == 0) {
799  /* Skip '.' (current dir) */
800  } else if (strcmp(pos, "..") == 0) {
801  /* level up */
802  if (dest[0] == '\0') {
803  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
804  break;
805  }
806 
807  /* Truncate 'dest' after last PATHSEPCHAR.
808  * This assumes that the truncated part is a real directory and not a link. */
809  destpos = strrchr(dest, PATHSEPCHAR);
810  if (destpos == nullptr) destpos = dest;
811  *destpos = '\0';
812  } else {
813  /* Append at end of 'dest' */
814  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
815  destpos = strecpy(destpos, pos, lastof(dest));
816  }
817 
818  if (destpos >= lastof(dest)) {
819  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename);
820  fclose(f);
821  return false;
822  }
823 
824  pos = next;
825  }
826 
827  /* Store links in temporary list */
828  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
829  links.insert(TarLinkList::value_type(name, dest));
830 
831  break;
832  }
833 
834  case '5': // directory
835  /* Convert to lowercase and our PATHSEPCHAR */
836  SimplifyFileName(name);
837 
838  /* Store the first directory name we detect */
839  DEBUG(misc, 6, "Found dir in tar: %s", name);
840  if (_tar_list[this->subdir][filename].dirname == nullptr) _tar_list[this->subdir][filename].dirname = stredup(name);
841  break;
842 
843  default:
844  /* Ignore other types */
845  break;
846  }
847 
848  /* Skip to the next block.. */
849  skip = Align(skip, 512);
850  if (fseek(f, skip, SEEK_CUR) < 0) {
851  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
852  fclose(f);
853  return false;
854  }
855  pos += skip;
856  }
857 
858  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
859  fclose(f);
860 
861  /* Resolve file links and store directory links.
862  * We restrict usage of links to two cases:
863  * 1) Links to directories:
864  * Both the source path and the destination path must NOT contain any further links.
865  * When resolving files at most one directory link is resolved.
866  * 2) Links to files:
867  * The destination path must NOT contain any links.
868  * The source path may contain one directory link.
869  */
870  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
871  const std::string &src = link->first;
872  const std::string &dest = link->second;
873  TarAddLink(src, dest, this->subdir);
874  }
875 
876  return true;
877 }
878 
886 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
887 {
888  TarList::iterator it = _tar_list[subdir].find(tar_filename);
889  /* We don't know the file. */
890  if (it == _tar_list[subdir].end()) return false;
891 
892  const char *dirname = (*it).second.dirname;
893 
894  /* The file doesn't have a sub directory! */
895  if (dirname == nullptr) return false;
896 
897  char filename[MAX_PATH];
898  strecpy(filename, tar_filename, lastof(filename));
899  char *p = strrchr(filename, PATHSEPCHAR);
900  /* The file's path does not have a separator? */
901  if (p == nullptr) return false;
902 
903  p++;
904  strecpy(p, dirname, lastof(filename));
905  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
906  FioCreateDirectory(filename);
907 
908  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
909  if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
910 
911  strecpy(p, (*it2).first.c_str(), lastof(filename));
912 
913  DEBUG(misc, 9, " extracting %s", filename);
914 
915  /* First open the file in the .tar. */
916  size_t to_copy = 0;
917  FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
918  if (in == nullptr) {
919  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
920  return false;
921  }
922 
923  /* Now open the 'output' file. */
924  FILE *out = fopen(filename, "wb");
925  if (out == nullptr) {
926  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
927  fclose(in);
928  return false;
929  }
930 
931  /* Now read from the tar and write it into the file. */
932  char buffer[4096];
933  size_t read;
934  for (; to_copy != 0; to_copy -= read) {
935  read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
936  if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
937  }
938 
939  /* Close everything up. */
940  fclose(in);
941  fclose(out);
942 
943  if (to_copy != 0) {
944  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
945  return false;
946  }
947  }
948 
949  DEBUG(misc, 9, " extraction successful");
950  return true;
951 }
952 
953 #if defined(_WIN32)
954 
959 extern void DetermineBasePaths(const char *exe);
960 #else /* defined(_WIN32) */
961 
969 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
970 {
971  char tmp[MAX_PATH];
972  strecpy(tmp, exe, lastof(tmp));
973 
974  bool success = false;
975 #ifdef WITH_COCOA
976  char *app_bundle = strchr(tmp, '.');
977  while (app_bundle != nullptr && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
978 
979  if (app_bundle != nullptr) *app_bundle = '\0';
980 #endif /* WITH_COCOA */
981  char *s = strrchr(tmp, PATHSEPCHAR);
982  if (s != nullptr) {
983  *s = '\0';
984  if (chdir(tmp) != 0) {
985  DEBUG(misc, 0, "Directory with the binary does not exist?");
986  } else {
987  success = true;
988  }
989  }
990  return success;
991 }
992 
1004 {
1005  /* No working directory, so nothing to do. */
1006  if (_searchpaths[SP_WORKING_DIR] == nullptr) return false;
1007 
1008  /* Working directory is root, so do nothing. */
1009  if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
1010 
1011  /* No personal/home directory, so the working directory won't be that. */
1012  if (_searchpaths[SP_PERSONAL_DIR] == nullptr) return true;
1013 
1014  char tmp[MAX_PATH];
1015  seprintf(tmp, lastof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
1016  AppendPathSeparator(tmp, lastof(tmp));
1017  return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
1018 }
1019 
1024 void DetermineBasePaths(const char *exe)
1025 {
1026  char tmp[MAX_PATH];
1027 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1028  const char *xdg_data_home = xdgDataHome(nullptr);
1029  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", xdg_data_home,
1030  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1031  free(xdg_data_home);
1032 
1033  AppendPathSeparator(tmp, lastof(tmp));
1034  _searchpaths[SP_PERSONAL_DIR_XDG] = stredup(tmp);
1035 #endif
1036 #if defined(OS2) || !defined(WITH_PERSONAL_DIR)
1037  _searchpaths[SP_PERSONAL_DIR] = nullptr;
1038 #else
1039 #ifdef __HAIKU__
1040  BPath path;
1041  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1042  const char *homedir = stredup(path.Path());
1043 #else
1044  /* getenv is highly unsafe; duplicate it as soon as possible,
1045  * or at least before something else touches the environment
1046  * variables in any way. It can also contain all kinds of
1047  * unvalidated data we rather not want internally. */
1048  const char *homedir = getenv("HOME");
1049  if (homedir != nullptr) {
1050  homedir = stredup(homedir);
1051  }
1052 
1053  if (homedir == nullptr) {
1054  const struct passwd *pw = getpwuid(getuid());
1055  homedir = (pw == nullptr) ? nullptr : stredup(pw->pw_dir);
1056  }
1057 #endif
1058 
1059  if (homedir != nullptr) {
1060  ValidateString(homedir);
1061  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
1062  AppendPathSeparator(tmp, lastof(tmp));
1063 
1065  free(homedir);
1066  } else {
1067  _searchpaths[SP_PERSONAL_DIR] = nullptr;
1068  }
1069 #endif
1070 
1071 #if defined(WITH_SHARED_DIR)
1072  seprintf(tmp, lastof(tmp), "%s", SHARED_DIR);
1073  AppendPathSeparator(tmp, lastof(tmp));
1075 #else
1076  _searchpaths[SP_SHARED_DIR] = nullptr;
1077 #endif
1078 
1079  if (getcwd(tmp, MAX_PATH) == nullptr) *tmp = '\0';
1080  AppendPathSeparator(tmp, lastof(tmp));
1082 
1084 
1085  /* Change the working directory to that one of the executable */
1087  if (getcwd(tmp, MAX_PATH) == nullptr) *tmp = '\0';
1088  AppendPathSeparator(tmp, lastof(tmp));
1090  } else {
1091  _searchpaths[SP_BINARY_DIR] = nullptr;
1092  }
1093 
1094  if (_searchpaths[SP_WORKING_DIR] != nullptr) {
1095  /* Go back to the current working directory. */
1096  if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
1097  DEBUG(misc, 0, "Failed to return to working directory!");
1098  }
1099  }
1100 
1101 #if !defined(GLOBAL_DATA_DIR)
1102  _searchpaths[SP_INSTALLATION_DIR] = nullptr;
1103 #else
1104  seprintf(tmp, lastof(tmp), "%s", GLOBAL_DATA_DIR);
1105  AppendPathSeparator(tmp, lastof(tmp));
1107 #endif
1108 #ifdef WITH_COCOA
1109 extern void cocoaSetApplicationBundleDir();
1110  cocoaSetApplicationBundleDir();
1111 #else
1113 #endif
1114 }
1115 #endif /* defined(_WIN32) */
1116 
1117 const char *_personal_dir;
1118 
1125 void DeterminePaths(const char *exe)
1126 {
1127  DetermineBasePaths(exe);
1128 
1129 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1130  char config_home[MAX_PATH];
1131 
1132  const char *xdg_config_home = xdgConfigHome(nullptr);
1133  seprintf(config_home, lastof(config_home), "%s" PATHSEP "%s", xdg_config_home,
1134  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1135  free(xdg_config_home);
1136 
1137  AppendPathSeparator(config_home, lastof(config_home));
1138 #endif
1139 
1140  Searchpath sp;
1141  FOR_ALL_SEARCHPATHS(sp) {
1142  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1143  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
1144  }
1145 
1146  char *config_dir;
1147  if (_config_file != nullptr) {
1148  config_dir = stredup(_config_file);
1149  char *end = strrchr(config_dir, PATHSEPCHAR);
1150  if (end == nullptr) {
1151  config_dir[0] = '\0';
1152  } else {
1153  end[1] = '\0';
1154  }
1155  } else {
1156  char personal_dir[MAX_PATH];
1157  if (FioFindFullPath(personal_dir, lastof(personal_dir), BASE_DIR, "openttd.cfg") != nullptr) {
1158  char *end = strrchr(personal_dir, PATHSEPCHAR);
1159  if (end != nullptr) end[1] = '\0';
1160  config_dir = stredup(personal_dir);
1161  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1162  } else {
1163 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1164  /* No previous configuration file found. Use the configuration folder from XDG. */
1165  config_dir = config_home;
1166 #else
1167  static const Searchpath new_openttd_cfg_order[] = {
1169  };
1170 
1171  config_dir = nullptr;
1172  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1173  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1174  config_dir = stredup(_searchpaths[new_openttd_cfg_order[i]]);
1175  break;
1176  }
1177  }
1178  assert(config_dir != nullptr);
1179 #endif
1180  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1181  }
1182  }
1183 
1184  DEBUG(misc, 3, "%s found as config directory", config_dir);
1185 
1186  _highscore_file = str_fmt("%shs.dat", config_dir);
1187  extern char *_hotkeys_file;
1188  _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
1189  extern char *_windows_file;
1190  _windows_file = str_fmt("%swindows.cfg", config_dir);
1191 
1192 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1193  if (config_dir == config_home) {
1194  /* We are using the XDG configuration home for the config file,
1195  * then store the rest in the XDG data home folder. */
1196  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1198  } else
1199 #endif
1200  {
1201  _personal_dir = config_dir;
1202  }
1203 
1204  /* Make the necessary folders */
1205 #if defined(WITH_PERSONAL_DIR)
1206  FioCreateDirectory(config_dir);
1207  if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
1208 #endif
1209 
1210  DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
1211 
1212  static const Subdirectory default_subdirs[] = {
1214  };
1215 
1216  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1217  char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
1218  FioCreateDirectory(dir);
1219  free(dir);
1220  }
1221 
1222  /* If we have network we make a directory for the autodownloading of content */
1223  _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
1225 
1226  /* Create the directory for each of the types of content */
1228  for (uint i = 0; i < lengthof(dirs); i++) {
1229  char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
1230  FioCreateDirectory(tmp);
1231  free(tmp);
1232  }
1233 
1234  extern char *_log_file;
1235  _log_file = str_fmt("%sopenttd.log", _personal_dir);
1236 }
1237 
1243 {
1244  for (; *filename != '\0'; filename++) {
1245  switch (*filename) {
1246  /* The following characters are not allowed in filenames
1247  * on at least one of the supported operating systems: */
1248  case ':': case '\\': case '*': case '?': case '/':
1249  case '<': case '>': case '|': case '"':
1250  *filename = '_';
1251  break;
1252  }
1253  }
1254 }
1255 
1264 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
1265 {
1266  FILE *in = fopen(filename, "rb");
1267  if (in == nullptr) return nullptr;
1268 
1269  fseek(in, 0, SEEK_END);
1270  size_t len = ftell(in);
1271  fseek(in, 0, SEEK_SET);
1272  if (len > maxsize) {
1273  fclose(in);
1274  return nullptr;
1275  }
1276  byte *mem = MallocT<byte>(len + 1);
1277  mem[len] = 0;
1278  if (fread(mem, len, 1, in) != 1) {
1279  fclose(in);
1280  free(mem);
1281  return nullptr;
1282  }
1283  fclose(in);
1284 
1285  *lenp = len;
1286  return mem;
1287 }
1288 
1295 static bool MatchesExtension(const char *extension, const char *filename)
1296 {
1297  if (extension == nullptr) return true;
1298 
1299  const char *ext = strrchr(filename, extension[0]);
1300  return ext != nullptr && strcasecmp(ext, extension) == 0;
1301 }
1302 
1312 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1313 {
1314  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1315 
1316  uint num = 0;
1317  struct stat sb;
1318  struct dirent *dirent;
1319  DIR *dir;
1320 
1321  if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0;
1322 
1323  while ((dirent = readdir(dir)) != nullptr) {
1324  const char *d_name = FS2OTTD(dirent->d_name);
1325  char filename[MAX_PATH];
1326 
1327  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1328 
1329  seprintf(filename, lastof(filename), "%s%s", path, d_name);
1330 
1331  if (S_ISDIR(sb.st_mode)) {
1332  /* Directory */
1333  if (!recursive) continue;
1334  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1335  if (!AppendPathSeparator(filename, lastof(filename))) continue;
1336  num += ScanPath(fs, extension, filename, basepath_length, recursive);
1337  } else if (S_ISREG(sb.st_mode)) {
1338  /* File */
1339  if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, nullptr)) num++;
1340  }
1341  }
1342 
1343  closedir(dir);
1344 
1345  return num;
1346 }
1347 
1354 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1355 {
1356  uint num = 0;
1357  const char *filename = (*tar).first.c_str();
1358 
1359  if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1360 
1361  return num;
1362 }
1363 
1373 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1374 {
1375  this->subdir = sd;
1376 
1377  Searchpath sp;
1378  char path[MAX_PATH];
1379  TarFileList::iterator tar;
1380  uint num = 0;
1381 
1382  FOR_ALL_SEARCHPATHS(sp) {
1383  /* Don't search in the working directory */
1384  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1385 
1386  FioAppendDirectory(path, lastof(path), sp, sd);
1387  num += ScanPath(this, extension, path, strlen(path), recursive);
1388  }
1389 
1390  if (tars && sd != NO_DIRECTORY) {
1391  FOR_ALL_TARS(tar, sd) {
1392  num += ScanTar(this, extension, tar);
1393  }
1394  }
1395 
1396  switch (sd) {
1397  case BASESET_DIR:
1398  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1399  FALLTHROUGH;
1400  case NEWGRF_DIR:
1401  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1402  break;
1403 
1404  default: break;
1405  }
1406 
1407  return num;
1408 }
1409 
1418 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1419 {
1420  char path[MAX_PATH];
1421  strecpy(path, directory, lastof(path));
1422  if (!AppendPathSeparator(path, lastof(path))) return 0;
1423  return ScanPath(this, extension, path, strlen(path), recursive);
1424 }
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:1003
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:1373
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:1354
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:552
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:82
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:969
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:592
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:1242
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:1117
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:1024
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:1312
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:608
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:656
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:1125
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:1295
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:886
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:1264
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.
Definition: fileio.cpp:532
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