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] == NULL) {
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 != NULL);
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] != NULL) {
200  fclose(_fio.handles[slot]);
201 
202  free(_fio.shortnames[slot]);
203  _fio.shortnames[slot] = NULL;
204 
205  _fio.handles[slot] = NULL;
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] != NULL && _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 == NULL) 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 == NULL ? filename : t);
269  char *t2 = strrchr(_fio.shortnames[slot], '.');
270  if (t2 != NULL) *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 == NULL) 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 NULL;
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 = NULL;
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 NULL;
421 #endif
422 
423  f = fopen(buf, mode);
424 #if !defined(_WIN32)
425  if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
426  f = fopen(buf, mode);
427  }
428 #endif
429  if (f != NULL && filesize != NULL) {
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 == NULL) return f;
449 
450  if (fseek(f, entry->position, SEEK_SET) < 0) {
451  fclose(f);
452  return NULL;
453  }
454 
455  if (filesize != NULL) *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 = NULL;
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 != NULL || subdir == NO_DIRECTORY) break;
475  }
476 
477  /* We can only use .tar in case of data-dir, and read-mode */
478  if (f == NULL && 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 == NULL && subdir != NO_DIRECTORY) {
512  switch (subdir) {
513  case BASESET_DIR:
514  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
515  if (f != NULL) 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), NULL);
540 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
541  mkdir(OTTD2FS(name));
542 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
543  char buf[MAX_PATH];
544  strecpy(buf, name, lastof(buf));
545 
546  size_t len = strlen(name) - 1;
547  if (buf[len] == '/') {
548  buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail
549  }
550 
551  mkdir(OTTD2FS(buf), 0755);
552 #else
553  mkdir(OTTD2FS(name), 0755);
554 #endif
555 }
556 
564 bool AppendPathSeparator(char *buf, const char *last)
565 {
566  size_t s = strlen(buf);
567 
568  /* Length of string + path separator + '\0' */
569  if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
570  if (&buf[s] >= last) return false;
571 
572  seprintf(buf + s, last, "%c", PATHSEPCHAR);
573  }
574 
575  return true;
576 }
577 
583 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
584 {
585  TarList::iterator it = _tar_list[subdir].find(tarname);
586  if (it == _tar_list[subdir].end()) return NULL;
587  return (*it).second.dirname;
588 }
589 
590 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
591 {
592  std::string src = srcParam;
593  std::string dest = destParam;
594  /* Tar internals assume lowercase */
595  std::transform(src.begin(), src.end(), src.begin(), tolower);
596  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
597 
598  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
599  if (dest_file != _tar_filelist[subdir].end()) {
600  /* Link to file. Process the link like the destination file. */
601  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
602  } else {
603  /* Destination file not found. Assume 'link to directory'
604  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
605  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
606  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
607  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
608  }
609 }
610 
611 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
612 {
613  TarAddLink(src, dest, subdir);
614 }
615 
621 static void SimplifyFileName(char *name)
622 {
623  /* Force lowercase */
624  strtolower(name);
625 
626  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
627 #if (PATHSEPCHAR != '/')
628  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
629 #endif
630 }
631 
638 {
639  _tar_filelist[sd].clear();
640  _tar_list[sd].clear();
641  uint num = this->Scan(".tar", sd, false);
642  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
643  return num;
644 }
645 
646 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
647 {
648  DEBUG(misc, 1, "Scanning for tars");
649  TarScanner fs;
650  uint num = 0;
651  if (mode & TarScanner::BASESET) {
652  num += fs.DoScan(BASESET_DIR);
653  }
654  if (mode & TarScanner::NEWGRF) {
655  num += fs.DoScan(NEWGRF_DIR);
656  }
657  if (mode & TarScanner::AI) {
658  num += fs.DoScan(AI_DIR);
659  num += fs.DoScan(AI_LIBRARY_DIR);
660  }
661  if (mode & TarScanner::GAME) {
662  num += fs.DoScan(GAME_DIR);
663  num += fs.DoScan(GAME_LIBRARY_DIR);
664  }
665  if (mode & TarScanner::SCENARIO) {
666  num += fs.DoScan(SCENARIO_DIR);
667  num += fs.DoScan(HEIGHTMAP_DIR);
668  }
669  DEBUG(misc, 1, "Scan complete, found %d files", num);
670  return num;
671 }
672 
680 {
681  this->subdir = sd;
682  return this->AddFile(filename, 0);
683 }
684 
685 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
686 {
687  /* No tar within tar. */
688  assert(tar_filename == NULL);
689 
690  /* The TAR-header, repeated for every file */
691  struct TarHeader {
692  char name[100];
693  char mode[8];
694  char uid[8];
695  char gid[8];
696  char size[12];
697  char mtime[12];
698  char chksum[8];
699  char typeflag;
700  char linkname[100];
701  char magic[6];
702  char version[2];
703  char uname[32];
704  char gname[32];
705  char devmajor[8];
706  char devminor[8];
707  char prefix[155];
708 
709  char unused[12];
710  };
711 
712  /* Check if we already seen this file */
713  TarList::iterator it = _tar_list[this->subdir].find(filename);
714  if (it != _tar_list[this->subdir].end()) return false;
715 
716  FILE *f = fopen(filename, "rb");
717  /* Although the file has been found there can be
718  * a number of reasons we cannot open the file.
719  * Most common case is when we simply have not
720  * been given read access. */
721  if (f == NULL) return false;
722 
723  const char *dupped_filename = stredup(filename);
724  _tar_list[this->subdir][filename].filename = dupped_filename;
725  _tar_list[this->subdir][filename].dirname = NULL;
726 
727  TarLinkList links;
728 
729  TarHeader th;
730  char buf[sizeof(th.name) + 1], *end;
731  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
732  char link[sizeof(th.linkname) + 1];
733  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
734  size_t num = 0, pos = 0;
735 
736  /* Make a char of 512 empty bytes */
737  char empty[512];
738  memset(&empty[0], 0, sizeof(empty));
739 
740  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
741  size_t num_bytes_read = fread(&th, 1, 512, f);
742  if (num_bytes_read != 512) break;
743  pos += num_bytes_read;
744 
745  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
746  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
747  /* If we have only zeros in the block, it can be an end-of-file indicator */
748  if (memcmp(&th, &empty[0], 512) == 0) continue;
749 
750  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
751  fclose(f);
752  return false;
753  }
754 
755  name[0] = '\0';
756 
757  /* The prefix contains the directory-name */
758  if (th.prefix[0] != '\0') {
759  strecpy(name, th.prefix, lastof(name));
760  strecat(name, PATHSEP, lastof(name));
761  }
762 
763  /* Copy the name of the file in a safe way at the end of 'name' */
764  strecat(name, th.name, lastof(name));
765 
766  /* Calculate the size of the file.. for some strange reason this is stored as a string */
767  strecpy(buf, th.size, lastof(buf));
768  size_t skip = strtoul(buf, &end, 8);
769 
770  switch (th.typeflag) {
771  case '\0':
772  case '0': { // regular file
773  /* Ignore empty files */
774  if (skip == 0) break;
775 
776  if (strlen(name) == 0) break;
777 
778  /* Store this entry in the list */
779  TarFileListEntry entry;
780  entry.tar_filename = dupped_filename;
781  entry.size = skip;
782  entry.position = pos;
783 
784  /* Convert to lowercase and our PATHSEPCHAR */
785  SimplifyFileName(name);
786 
787  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
788  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
789 
790  break;
791  }
792 
793  case '1': // hard links
794  case '2': { // symbolic links
795  /* Copy the destination of the link in a safe way at the end of 'linkname' */
796  strecpy(link, th.linkname, lastof(link));
797 
798  if (strlen(name) == 0 || strlen(link) == 0) break;
799 
800  /* Convert to lowercase and our PATHSEPCHAR */
801  SimplifyFileName(name);
802  SimplifyFileName(link);
803 
804  /* Only allow relative links */
805  if (link[0] == PATHSEPCHAR) {
806  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
807  break;
808  }
809 
810  /* Process relative path.
811  * Note: The destination of links must not contain any directory-links. */
812  strecpy(dest, name, lastof(dest));
813  char *destpos = strrchr(dest, PATHSEPCHAR);
814  if (destpos == NULL) destpos = dest;
815  *destpos = '\0';
816 
817  char *pos = link;
818  while (*pos != '\0') {
819  char *next = strchr(pos, PATHSEPCHAR);
820  if (next == NULL) {
821  next = pos + strlen(pos);
822  } else {
823  /* Terminate the substring up to the path separator character. */
824  *next++= '\0';
825  }
826 
827  if (strcmp(pos, ".") == 0) {
828  /* Skip '.' (current dir) */
829  } else if (strcmp(pos, "..") == 0) {
830  /* level up */
831  if (dest[0] == '\0') {
832  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
833  break;
834  }
835 
836  /* Truncate 'dest' after last PATHSEPCHAR.
837  * This assumes that the truncated part is a real directory and not a link. */
838  destpos = strrchr(dest, PATHSEPCHAR);
839  if (destpos == NULL) destpos = dest;
840  *destpos = '\0';
841  } else {
842  /* Append at end of 'dest' */
843  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
844  destpos = strecpy(destpos, pos, lastof(dest));
845  }
846 
847  if (destpos >= lastof(dest)) {
848  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename);
849  fclose(f);
850  return false;
851  }
852 
853  pos = next;
854  }
855 
856  /* Store links in temporary list */
857  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
858  links.insert(TarLinkList::value_type(name, dest));
859 
860  break;
861  }
862 
863  case '5': // directory
864  /* Convert to lowercase and our PATHSEPCHAR */
865  SimplifyFileName(name);
866 
867  /* Store the first directory name we detect */
868  DEBUG(misc, 6, "Found dir in tar: %s", name);
869  if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = stredup(name);
870  break;
871 
872  default:
873  /* Ignore other types */
874  break;
875  }
876 
877  /* Skip to the next block.. */
878  skip = Align(skip, 512);
879  if (fseek(f, skip, SEEK_CUR) < 0) {
880  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
881  fclose(f);
882  return false;
883  }
884  pos += skip;
885  }
886 
887  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
888  fclose(f);
889 
890  /* Resolve file links and store directory links.
891  * We restrict usage of links to two cases:
892  * 1) Links to directories:
893  * Both the source path and the destination path must NOT contain any further links.
894  * When resolving files at most one directory link is resolved.
895  * 2) Links to files:
896  * The destination path must NOT contain any links.
897  * The source path may contain one directory link.
898  */
899  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
900  const std::string &src = link->first;
901  const std::string &dest = link->second;
902  TarAddLink(src, dest, this->subdir);
903  }
904 
905  return true;
906 }
907 
915 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
916 {
917  TarList::iterator it = _tar_list[subdir].find(tar_filename);
918  /* We don't know the file. */
919  if (it == _tar_list[subdir].end()) return false;
920 
921  const char *dirname = (*it).second.dirname;
922 
923  /* The file doesn't have a sub directory! */
924  if (dirname == NULL) return false;
925 
926  char filename[MAX_PATH];
927  strecpy(filename, tar_filename, lastof(filename));
928  char *p = strrchr(filename, PATHSEPCHAR);
929  /* The file's path does not have a separator? */
930  if (p == NULL) return false;
931 
932  p++;
933  strecpy(p, dirname, lastof(filename));
934  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
935  FioCreateDirectory(filename);
936 
937  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
938  if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
939 
940  strecpy(p, (*it2).first.c_str(), lastof(filename));
941 
942  DEBUG(misc, 9, " extracting %s", filename);
943 
944  /* First open the file in the .tar. */
945  size_t to_copy = 0;
946  FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
947  if (in == NULL) {
948  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
949  return false;
950  }
951 
952  /* Now open the 'output' file. */
953  FILE *out = fopen(filename, "wb");
954  if (out == NULL) {
955  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
956  fclose(in);
957  return false;
958  }
959 
960  /* Now read from the tar and write it into the file. */
961  char buffer[4096];
962  size_t read;
963  for (; to_copy != 0; to_copy -= read) {
964  read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
965  if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
966  }
967 
968  /* Close everything up. */
969  fclose(in);
970  fclose(out);
971 
972  if (to_copy != 0) {
973  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
974  return false;
975  }
976  }
977 
978  DEBUG(misc, 9, " extraction successful");
979  return true;
980 }
981 
982 #if defined(_WIN32)
983 
988 extern void DetermineBasePaths(const char *exe);
989 #else /* defined(_WIN32) */
990 
998 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
999 {
1000  char tmp[MAX_PATH];
1001  strecpy(tmp, exe, lastof(tmp));
1002 
1003  bool success = false;
1004 #ifdef WITH_COCOA
1005  char *app_bundle = strchr(tmp, '.');
1006  while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
1007 
1008  if (app_bundle != NULL) *app_bundle = '\0';
1009 #endif /* WITH_COCOA */
1010  char *s = strrchr(tmp, PATHSEPCHAR);
1011  if (s != NULL) {
1012  *s = '\0';
1013 #if defined(__DJGPP__)
1014  /* If we want to go to the root, we can't use cd C:, but we must use '/' */
1015  if (s > tmp && *(s - 1) == ':') chdir("/");
1016 #endif
1017  if (chdir(tmp) != 0) {
1018  DEBUG(misc, 0, "Directory with the binary does not exist?");
1019  } else {
1020  success = true;
1021  }
1022  }
1023  return success;
1024 }
1025 
1037 {
1038  /* No working directory, so nothing to do. */
1039  if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
1040 
1041  /* Working directory is root, so do nothing. */
1042  if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
1043 
1044  /* No personal/home directory, so the working directory won't be that. */
1045  if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
1046 
1047  char tmp[MAX_PATH];
1048  seprintf(tmp, lastof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
1049  AppendPathSeparator(tmp, lastof(tmp));
1050  return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
1051 }
1052 
1057 void DetermineBasePaths(const char *exe)
1058 {
1059  char tmp[MAX_PATH];
1060 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1061  const char *xdg_data_home = xdgDataHome(NULL);
1062  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", xdg_data_home,
1063  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1064  free(xdg_data_home);
1065 
1066  AppendPathSeparator(tmp, lastof(tmp));
1067  _searchpaths[SP_PERSONAL_DIR_XDG] = stredup(tmp);
1068 #endif
1069 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
1070  _searchpaths[SP_PERSONAL_DIR] = NULL;
1071 #else
1072 #ifdef __HAIKU__
1073  BPath path;
1074  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1075  const char *homedir = stredup(path.Path());
1076 #else
1077  /* getenv is highly unsafe; duplicate it as soon as possible,
1078  * or at least before something else touches the environment
1079  * variables in any way. It can also contain all kinds of
1080  * unvalidated data we rather not want internally. */
1081  const char *homedir = getenv("HOME");
1082  if (homedir != NULL) {
1083  homedir = stredup(homedir);
1084  }
1085 
1086  if (homedir == NULL) {
1087  const struct passwd *pw = getpwuid(getuid());
1088  homedir = (pw == NULL) ? NULL : stredup(pw->pw_dir);
1089  }
1090 #endif
1091 
1092  if (homedir != NULL) {
1093  ValidateString(homedir);
1094  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
1095  AppendPathSeparator(tmp, lastof(tmp));
1096 
1098  free(homedir);
1099  } else {
1100  _searchpaths[SP_PERSONAL_DIR] = NULL;
1101  }
1102 #endif
1103 
1104 #if defined(WITH_SHARED_DIR)
1105  seprintf(tmp, lastof(tmp), "%s", SHARED_DIR);
1106  AppendPathSeparator(tmp, lastof(tmp));
1108 #else
1109  _searchpaths[SP_SHARED_DIR] = NULL;
1110 #endif
1111 
1112 #if defined(__MORPHOS__) || defined(__AMIGA__)
1113  _searchpaths[SP_WORKING_DIR] = NULL;
1114 #else
1115  if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
1116  AppendPathSeparator(tmp, lastof(tmp));
1118 #endif
1119 
1121 
1122  /* Change the working directory to that one of the executable */
1124  if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
1125  AppendPathSeparator(tmp, lastof(tmp));
1127  } else {
1128  _searchpaths[SP_BINARY_DIR] = NULL;
1129  }
1130 
1131  if (_searchpaths[SP_WORKING_DIR] != NULL) {
1132  /* Go back to the current working directory. */
1133  if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
1134  DEBUG(misc, 0, "Failed to return to working directory!");
1135  }
1136  }
1137 
1138 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
1140 #else
1141  seprintf(tmp, lastof(tmp), "%s", GLOBAL_DATA_DIR);
1142  AppendPathSeparator(tmp, lastof(tmp));
1144 #endif
1145 #ifdef WITH_COCOA
1146 extern void cocoaSetApplicationBundleDir();
1147  cocoaSetApplicationBundleDir();
1148 #else
1150 #endif
1151 }
1152 #endif /* defined(_WIN32) */
1153 
1154 const char *_personal_dir;
1155 
1162 void DeterminePaths(const char *exe)
1163 {
1164  DetermineBasePaths(exe);
1165 
1166 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1167  char config_home[MAX_PATH];
1168 
1169  const char *xdg_config_home = xdgConfigHome(NULL);
1170  seprintf(config_home, lastof(config_home), "%s" PATHSEP "%s", xdg_config_home,
1171  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1172  free(xdg_config_home);
1173 
1174  AppendPathSeparator(config_home, lastof(config_home));
1175 #endif
1176 
1177  Searchpath sp;
1178  FOR_ALL_SEARCHPATHS(sp) {
1179  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1180  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
1181  }
1182 
1183  char *config_dir;
1184  if (_config_file != NULL) {
1185  config_dir = stredup(_config_file);
1186  char *end = strrchr(config_dir, PATHSEPCHAR);
1187  if (end == NULL) {
1188  config_dir[0] = '\0';
1189  } else {
1190  end[1] = '\0';
1191  }
1192  } else {
1193  char personal_dir[MAX_PATH];
1194  if (FioFindFullPath(personal_dir, lastof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
1195  char *end = strrchr(personal_dir, PATHSEPCHAR);
1196  if (end != NULL) end[1] = '\0';
1197  config_dir = stredup(personal_dir);
1198  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1199  } else {
1200 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1201  /* No previous configuration file found. Use the configuration folder from XDG. */
1202  config_dir = config_home;
1203 #else
1204  static const Searchpath new_openttd_cfg_order[] = {
1206  };
1207 
1208  config_dir = NULL;
1209  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1210  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1211  config_dir = stredup(_searchpaths[new_openttd_cfg_order[i]]);
1212  break;
1213  }
1214  }
1215  assert(config_dir != NULL);
1216 #endif
1217  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1218  }
1219  }
1220 
1221  DEBUG(misc, 3, "%s found as config directory", config_dir);
1222 
1223  _highscore_file = str_fmt("%shs.dat", config_dir);
1224  extern char *_hotkeys_file;
1225  _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
1226  extern char *_windows_file;
1227  _windows_file = str_fmt("%swindows.cfg", config_dir);
1228 
1229 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1230  if (config_dir == config_home) {
1231  /* We are using the XDG configuration home for the config file,
1232  * then store the rest in the XDG data home folder. */
1233  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1234  FioCreateDirectory(_personal_dir);
1235  } else
1236 #endif
1237  {
1238  _personal_dir = config_dir;
1239  }
1240 
1241  /* Make the necessary folders */
1242 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
1243  FioCreateDirectory(config_dir);
1244  if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
1245 #endif
1246 
1247  DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
1248 
1249  static const Subdirectory default_subdirs[] = {
1251  };
1252 
1253  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1254  char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
1255  FioCreateDirectory(dir);
1256  free(dir);
1257  }
1258 
1259  /* If we have network we make a directory for the autodownloading of content */
1260  _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
1261 #ifdef ENABLE_NETWORK
1263 
1264  /* Create the directory for each of the types of content */
1266  for (uint i = 0; i < lengthof(dirs); i++) {
1267  char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
1268  FioCreateDirectory(tmp);
1269  free(tmp);
1270  }
1271 
1272  extern char *_log_file;
1273  _log_file = str_fmt("%sopenttd.log", _personal_dir);
1274 #else /* ENABLE_NETWORK */
1275  /* If we don't have networking, we don't need to make the directory. But
1276  * if it exists we keep it, otherwise remove it from the search paths. */
1277  if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
1278  free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1280  }
1281 #endif /* ENABLE_NETWORK */
1282 }
1283 
1289 {
1290  for (; *filename != '\0'; filename++) {
1291  switch (*filename) {
1292  /* The following characters are not allowed in filenames
1293  * on at least one of the supported operating systems: */
1294  case ':': case '\\': case '*': case '?': case '/':
1295  case '<': case '>': case '|': case '"':
1296  *filename = '_';
1297  break;
1298  }
1299  }
1300 }
1301 
1310 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
1311 {
1312  FILE *in = fopen(filename, "rb");
1313  if (in == NULL) return NULL;
1314 
1315  fseek(in, 0, SEEK_END);
1316  size_t len = ftell(in);
1317  fseek(in, 0, SEEK_SET);
1318  if (len > maxsize) {
1319  fclose(in);
1320  return NULL;
1321  }
1322  byte *mem = MallocT<byte>(len + 1);
1323  mem[len] = 0;
1324  if (fread(mem, len, 1, in) != 1) {
1325  fclose(in);
1326  free(mem);
1327  return NULL;
1328  }
1329  fclose(in);
1330 
1331  *lenp = len;
1332  return mem;
1333 }
1334 
1341 static bool MatchesExtension(const char *extension, const char *filename)
1342 {
1343  if (extension == NULL) return true;
1344 
1345  const char *ext = strrchr(filename, extension[0]);
1346  return ext != NULL && strcasecmp(ext, extension) == 0;
1347 }
1348 
1358 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1359 {
1360  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1361 
1362  uint num = 0;
1363  struct stat sb;
1364  struct dirent *dirent;
1365  DIR *dir;
1366 
1367  if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
1368 
1369  while ((dirent = readdir(dir)) != NULL) {
1370  const char *d_name = FS2OTTD(dirent->d_name);
1371  char filename[MAX_PATH];
1372 
1373  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1374 
1375  seprintf(filename, lastof(filename), "%s%s", path, d_name);
1376 
1377  if (S_ISDIR(sb.st_mode)) {
1378  /* Directory */
1379  if (!recursive) continue;
1380  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1381  if (!AppendPathSeparator(filename, lastof(filename))) continue;
1382  num += ScanPath(fs, extension, filename, basepath_length, recursive);
1383  } else if (S_ISREG(sb.st_mode)) {
1384  /* File */
1385  if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
1386  }
1387  }
1388 
1389  closedir(dir);
1390 
1391  return num;
1392 }
1393 
1400 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1401 {
1402  uint num = 0;
1403  const char *filename = (*tar).first.c_str();
1404 
1405  if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1406 
1407  return num;
1408 }
1409 
1419 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1420 {
1421  this->subdir = sd;
1422 
1423  Searchpath sp;
1424  char path[MAX_PATH];
1425  TarFileList::iterator tar;
1426  uint num = 0;
1427 
1428  FOR_ALL_SEARCHPATHS(sp) {
1429  /* Don't search in the working directory */
1430  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1431 
1432  FioAppendDirectory(path, lastof(path), sp, sd);
1433  num += ScanPath(this, extension, path, strlen(path), recursive);
1434  }
1435 
1436  if (tars && sd != NO_DIRECTORY) {
1437  FOR_ALL_TARS(tar, sd) {
1438  num += ScanTar(this, extension, tar);
1439  }
1440  }
1441 
1442  switch (sd) {
1443  case BASESET_DIR:
1444  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1445  FALLTHROUGH;
1446  case NEWGRF_DIR:
1447  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1448  break;
1449 
1450  default: break;
1451  }
1452 
1453  return num;
1454 }
1455 
1464 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1465 {
1466  char path[MAX_PATH];
1467  strecpy(path, directory, lastof(path));
1468  if (!AppendPathSeparator(path, lastof(path))) return 0;
1469  return ScanPath(this, extension, path, strlen(path), recursive);
1470 }
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:1036
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:102
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:565
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:1419
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:96
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:1400
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:74
char * _log_file
File to reroute output of a forked OpenTTD to.
Definition: dedicated.cpp:16
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:564
Scan for game scripts.
Definition: fileio_func.h:106
Definition: win32.cpp:93
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Definition: math_func.hpp:97
bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename=NULL)
Add a file with the given filename.
Definition: fileio.cpp:685
char * _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:80
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:998
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:103
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:621
Scan for scenarios and heightmaps.
Definition: fileio_func.h:105
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:1288
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:1154
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:1057
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:583
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:1358
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:637
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:36
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
const char * FioTarFirstDir(const char *tarname, Subdirectory subdir)
Find the first directory in a tar archive.
Definition: fileio.cpp:583
Mode
The mode of tar scanning.
Definition: fileio_func.h:100
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:1162
char * _highscore_file
The file to store the highscore data in.
Definition: highscore.cpp:26
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:1341
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:148
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:104
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:915
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:114
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:1310
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