OpenTTD
fileio.cpp
Go to the documentation of this file.
1 /* $Id: fileio.cpp 27954 2017-12-27 22:07:59Z frosch $ */
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 #if defined(WINCE)
329  /* There is always one platform that doesn't support basic commands... */
330  HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
331  if (hand == INVALID_HANDLE_VALUE) return 1;
332  CloseHandle(hand);
333  return 0;
334 #else
335  return access(OTTD2FS(filename), 0) == 0;
336 #endif
337 }
338 
342 void FioFCloseFile(FILE *f)
343 {
344  fclose(f);
345 }
346 
347 char *FioGetFullPath(char *buf, const char *last, Searchpath sp, Subdirectory subdir, const char *filename)
348 {
349  assert(subdir < NUM_SUBDIRS);
350  assert(sp < NUM_SEARCHPATHS);
351 
352  seprintf(buf, last, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
353  return buf;
354 }
355 
364 char *FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
365 {
366  Searchpath sp;
367  assert(subdir < NUM_SUBDIRS);
368 
369  FOR_ALL_SEARCHPATHS(sp) {
370  FioGetFullPath(buf, last, sp, subdir, filename);
371  if (FileExists(buf)) return buf;
372 #if !defined(WIN32)
373  /* Be, as opening files, aware that sometimes the filename
374  * might be in uppercase when it is in lowercase on the
375  * disk. Of course Windows doesn't care about casing. */
376  if (strtolower(buf + strlen(_searchpaths[sp]) - 1) && FileExists(buf)) return buf;
377 #endif
378  }
379 
380  return NULL;
381 }
382 
383 char *FioAppendDirectory(char *buf, const char *last, Searchpath sp, Subdirectory subdir)
384 {
385  assert(subdir < NUM_SUBDIRS);
386  assert(sp < NUM_SEARCHPATHS);
387 
388  seprintf(buf, last, "%s%s", _searchpaths[sp], _subdirs[subdir]);
389  return buf;
390 }
391 
392 char *FioGetDirectory(char *buf, const char *last, Subdirectory subdir)
393 {
394  Searchpath sp;
395 
396  /* Find and return the first valid directory */
397  FOR_ALL_SEARCHPATHS(sp) {
398  char *ret = FioAppendDirectory(buf, last, sp, subdir);
399  if (FileExists(buf)) return ret;
400  }
401 
402  /* Could not find the directory, fall back to a base path */
403  strecpy(buf, _personal_dir, last);
404 
405  return buf;
406 }
407 
408 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
409 {
410 #if defined(WIN32) && defined(UNICODE)
411  /* fopen is implemented as a define with ellipses for
412  * Unicode support (prepend an L). As we are not sending
413  * a string, but a variable, it 'renames' the variable,
414  * so make that variable to makes it compile happily */
415  wchar_t Lmode[5];
416  MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
417 #endif
418  FILE *f = NULL;
419  char buf[MAX_PATH];
420 
421  if (subdir == NO_DIRECTORY) {
422  strecpy(buf, filename, lastof(buf));
423  } else {
424  seprintf(buf, lastof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
425  }
426 
427 #if defined(WIN32)
428  if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
429 #endif
430 
431  f = fopen(buf, mode);
432 #if !defined(WIN32)
433  if (f == NULL && strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1))) {
434  f = fopen(buf, mode);
435  }
436 #endif
437  if (f != NULL && filesize != NULL) {
438  /* Find the size of the file */
439  fseek(f, 0, SEEK_END);
440  *filesize = ftell(f);
441  fseek(f, 0, SEEK_SET);
442  }
443  return f;
444 }
445 
453 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
454 {
455  FILE *f = fopen(entry->tar_filename, "rb");
456  if (f == NULL) return f;
457 
458  if (fseek(f, entry->position, SEEK_SET) < 0) {
459  fclose(f);
460  return NULL;
461  }
462 
463  if (filesize != NULL) *filesize = entry->size;
464  return f;
465 }
466 
474 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
475 {
476  FILE *f = NULL;
477  Searchpath sp;
478 
479  assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
480 
481  FOR_ALL_SEARCHPATHS(sp) {
482  f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
483  if (f != NULL || subdir == NO_DIRECTORY) break;
484  }
485 
486  /* We can only use .tar in case of data-dir, and read-mode */
487  if (f == NULL && mode[0] == 'r' && subdir != NO_DIRECTORY) {
488  static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
489  char resolved_name[MAX_RESOLVED_LENGTH];
490 
491  /* Filenames in tars are always forced to be lowercase */
492  strecpy(resolved_name, filename, lastof(resolved_name));
493  strtolower(resolved_name);
494 
495  size_t resolved_len = strlen(resolved_name);
496 
497  /* Resolve ONE directory link */
498  for (TarLinkList::iterator link = _tar_linklist[subdir].begin(); link != _tar_linklist[subdir].end(); link++) {
499  const std::string &src = link->first;
500  size_t len = src.length();
501  if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
502  /* Apply link */
503  char resolved_name2[MAX_RESOLVED_LENGTH];
504  const std::string &dest = link->second;
505  strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
506  strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
507  strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
508  break; // Only resolve one level
509  }
510  }
511 
512  TarFileList::iterator it = _tar_filelist[subdir].find(resolved_name);
513  if (it != _tar_filelist[subdir].end()) {
514  f = FioFOpenFileTar(&((*it).second), filesize);
515  }
516  }
517 
518  /* Sometimes a full path is given. To support
519  * the 'subdirectory' must be 'removed'. */
520  if (f == NULL && subdir != NO_DIRECTORY) {
521  switch (subdir) {
522  case BASESET_DIR:
523  f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
524  if (f != NULL) break;
525  FALLTHROUGH;
526  case NEWGRF_DIR:
527  f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
528  break;
529 
530  default:
531  f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
532  break;
533  }
534  }
535 
536  return f;
537 }
538 
543 static void FioCreateDirectory(const char *name)
544 {
545  /* Ignore directory creation errors; they'll surface later on, and most
546  * of the time they are 'directory already exists' errors anyhow. */
547 #if defined(WIN32) || defined(WINCE)
548  CreateDirectory(OTTD2FS(name), NULL);
549 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
550  mkdir(OTTD2FS(name));
551 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
552  char buf[MAX_PATH];
553  strecpy(buf, name, lastof(buf));
554 
555  size_t len = strlen(name) - 1;
556  if (buf[len] == '/') {
557  buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail
558  }
559 
560  mkdir(OTTD2FS(buf), 0755);
561 #else
562  mkdir(OTTD2FS(name), 0755);
563 #endif
564 }
565 
573 bool AppendPathSeparator(char *buf, const char *last)
574 {
575  size_t s = strlen(buf);
576 
577  /* Length of string + path separator + '\0' */
578  if (s != 0 && buf[s - 1] != PATHSEPCHAR) {
579  if (&buf[s] >= last) return false;
580 
581  seprintf(buf + s, last, "%c", PATHSEPCHAR);
582  }
583 
584  return true;
585 }
586 
592 const char *FioTarFirstDir(const char *tarname, Subdirectory subdir)
593 {
594  TarList::iterator it = _tar_list[subdir].find(tarname);
595  if (it == _tar_list[subdir].end()) return NULL;
596  return (*it).second.dirname;
597 }
598 
599 static void TarAddLink(const std::string &srcParam, const std::string &destParam, Subdirectory subdir)
600 {
601  std::string src = srcParam;
602  std::string dest = destParam;
603  /* Tar internals assume lowercase */
604  std::transform(src.begin(), src.end(), src.begin(), tolower);
605  std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
606 
607  TarFileList::iterator dest_file = _tar_filelist[subdir].find(dest);
608  if (dest_file != _tar_filelist[subdir].end()) {
609  /* Link to file. Process the link like the destination file. */
610  _tar_filelist[subdir].insert(TarFileList::value_type(src, dest_file->second));
611  } else {
612  /* Destination file not found. Assume 'link to directory'
613  * Append PATHSEPCHAR to 'src' and 'dest' if needed */
614  const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
615  const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
616  _tar_linklist[subdir].insert(TarLinkList::value_type(src_path, dst_path));
617  }
618 }
619 
620 void FioTarAddLink(const char *src, const char *dest, Subdirectory subdir)
621 {
622  TarAddLink(src, dest, subdir);
623 }
624 
630 static void SimplifyFileName(char *name)
631 {
632  /* Force lowercase */
633  strtolower(name);
634 
635  /* Tar-files always have '/' path-separator, but we want our PATHSEPCHAR */
636 #if (PATHSEPCHAR != '/')
637  for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
638 #endif
639 }
640 
647 {
648  _tar_filelist[sd].clear();
649  _tar_list[sd].clear();
650  uint num = this->Scan(".tar", sd, false);
651  if (sd == BASESET_DIR || sd == NEWGRF_DIR) num += this->Scan(".tar", OLD_DATA_DIR, false);
652  return num;
653 }
654 
655 /* static */ uint TarScanner::DoScan(TarScanner::Mode mode)
656 {
657  DEBUG(misc, 1, "Scanning for tars");
658  TarScanner fs;
659  uint num = 0;
660  if (mode & TarScanner::BASESET) {
661  num += fs.DoScan(BASESET_DIR);
662  }
663  if (mode & TarScanner::NEWGRF) {
664  num += fs.DoScan(NEWGRF_DIR);
665  }
666  if (mode & TarScanner::AI) {
667  num += fs.DoScan(AI_DIR);
668  num += fs.DoScan(AI_LIBRARY_DIR);
669  }
670  if (mode & TarScanner::GAME) {
671  num += fs.DoScan(GAME_DIR);
672  num += fs.DoScan(GAME_LIBRARY_DIR);
673  }
674  if (mode & TarScanner::SCENARIO) {
675  num += fs.DoScan(SCENARIO_DIR);
676  num += fs.DoScan(HEIGHTMAP_DIR);
677  }
678  DEBUG(misc, 1, "Scan complete, found %d files", num);
679  return num;
680 }
681 
689 {
690  this->subdir = sd;
691  return this->AddFile(filename, 0);
692 }
693 
694 bool TarScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
695 {
696  /* No tar within tar. */
697  assert(tar_filename == NULL);
698 
699  /* The TAR-header, repeated for every file */
700  struct TarHeader {
701  char name[100];
702  char mode[8];
703  char uid[8];
704  char gid[8];
705  char size[12];
706  char mtime[12];
707  char chksum[8];
708  char typeflag;
709  char linkname[100];
710  char magic[6];
711  char version[2];
712  char uname[32];
713  char gname[32];
714  char devmajor[8];
715  char devminor[8];
716  char prefix[155];
717 
718  char unused[12];
719  };
720 
721  /* Check if we already seen this file */
722  TarList::iterator it = _tar_list[this->subdir].find(filename);
723  if (it != _tar_list[this->subdir].end()) return false;
724 
725  FILE *f = fopen(filename, "rb");
726  /* Although the file has been found there can be
727  * a number of reasons we cannot open the file.
728  * Most common case is when we simply have not
729  * been given read access. */
730  if (f == NULL) return false;
731 
732  const char *dupped_filename = stredup(filename);
733  _tar_list[this->subdir][filename].filename = dupped_filename;
734  _tar_list[this->subdir][filename].dirname = NULL;
735 
736  TarLinkList links;
737 
738  TarHeader th;
739  char buf[sizeof(th.name) + 1], *end;
740  char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
741  char link[sizeof(th.linkname) + 1];
742  char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
743  size_t num = 0, pos = 0;
744 
745  /* Make a char of 512 empty bytes */
746  char empty[512];
747  memset(&empty[0], 0, sizeof(empty));
748 
749  for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
750  size_t num_bytes_read = fread(&th, 1, 512, f);
751  if (num_bytes_read != 512) break;
752  pos += num_bytes_read;
753 
754  /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
755  if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
756  /* If we have only zeros in the block, it can be an end-of-file indicator */
757  if (memcmp(&th, &empty[0], 512) == 0) continue;
758 
759  DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
760  fclose(f);
761  return false;
762  }
763 
764  name[0] = '\0';
765 
766  /* The prefix contains the directory-name */
767  if (th.prefix[0] != '\0') {
768  strecpy(name, th.prefix, lastof(name));
769  strecat(name, PATHSEP, lastof(name));
770  }
771 
772  /* Copy the name of the file in a safe way at the end of 'name' */
773  strecat(name, th.name, lastof(name));
774 
775  /* Calculate the size of the file.. for some strange reason this is stored as a string */
776  strecpy(buf, th.size, lastof(buf));
777  size_t skip = strtoul(buf, &end, 8);
778 
779  switch (th.typeflag) {
780  case '\0':
781  case '0': { // regular file
782  /* Ignore empty files */
783  if (skip == 0) break;
784 
785  if (strlen(name) == 0) break;
786 
787  /* Store this entry in the list */
788  TarFileListEntry entry;
789  entry.tar_filename = dupped_filename;
790  entry.size = skip;
791  entry.position = pos;
792 
793  /* Convert to lowercase and our PATHSEPCHAR */
794  SimplifyFileName(name);
795 
796  DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
797  if (_tar_filelist[this->subdir].insert(TarFileList::value_type(name, entry)).second) num++;
798 
799  break;
800  }
801 
802  case '1': // hard links
803  case '2': { // symbolic links
804  /* Copy the destination of the link in a safe way at the end of 'linkname' */
805  strecpy(link, th.linkname, lastof(link));
806 
807  if (strlen(name) == 0 || strlen(link) == 0) break;
808 
809  /* Convert to lowercase and our PATHSEPCHAR */
810  SimplifyFileName(name);
811  SimplifyFileName(link);
812 
813  /* Only allow relative links */
814  if (link[0] == PATHSEPCHAR) {
815  DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
816  break;
817  }
818 
819  /* Process relative path.
820  * Note: The destination of links must not contain any directory-links. */
821  strecpy(dest, name, lastof(dest));
822  char *destpos = strrchr(dest, PATHSEPCHAR);
823  if (destpos == NULL) destpos = dest;
824  *destpos = '\0';
825 
826  char *pos = link;
827  while (*pos != '\0') {
828  char *next = strchr(pos, PATHSEPCHAR);
829  if (next == NULL) {
830  next = pos + strlen(pos);
831  } else {
832  /* Terminate the substring up to the path separator character. */
833  *next++= '\0';
834  }
835 
836  if (strcmp(pos, ".") == 0) {
837  /* Skip '.' (current dir) */
838  } else if (strcmp(pos, "..") == 0) {
839  /* level up */
840  if (dest[0] == '\0') {
841  DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
842  break;
843  }
844 
845  /* Truncate 'dest' after last PATHSEPCHAR.
846  * This assumes that the truncated part is a real directory and not a link. */
847  destpos = strrchr(dest, PATHSEPCHAR);
848  if (destpos == NULL) destpos = dest;
849  *destpos = '\0';
850  } else {
851  /* Append at end of 'dest' */
852  if (destpos != dest) destpos = strecpy(destpos, PATHSEP, lastof(dest));
853  destpos = strecpy(destpos, pos, lastof(dest));
854  }
855 
856  if (destpos >= lastof(dest)) {
857  DEBUG(misc, 0, "The length of a link in tar-file '%s' is too large (malformed?)", filename);
858  fclose(f);
859  return false;
860  }
861 
862  pos = next;
863  }
864 
865  /* Store links in temporary list */
866  DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
867  links.insert(TarLinkList::value_type(name, dest));
868 
869  break;
870  }
871 
872  case '5': // directory
873  /* Convert to lowercase and our PATHSEPCHAR */
874  SimplifyFileName(name);
875 
876  /* Store the first directory name we detect */
877  DEBUG(misc, 6, "Found dir in tar: %s", name);
878  if (_tar_list[this->subdir][filename].dirname == NULL) _tar_list[this->subdir][filename].dirname = stredup(name);
879  break;
880 
881  default:
882  /* Ignore other types */
883  break;
884  }
885 
886  /* Skip to the next block.. */
887  skip = Align(skip, 512);
888  if (fseek(f, skip, SEEK_CUR) < 0) {
889  DEBUG(misc, 0, "The file '%s' can't be read as a valid tar-file", filename);
890  fclose(f);
891  return false;
892  }
893  pos += skip;
894  }
895 
896  DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
897  fclose(f);
898 
899  /* Resolve file links and store directory links.
900  * We restrict usage of links to two cases:
901  * 1) Links to directories:
902  * Both the source path and the destination path must NOT contain any further links.
903  * When resolving files at most one directory link is resolved.
904  * 2) Links to files:
905  * The destination path must NOT contain any links.
906  * The source path may contain one directory link.
907  */
908  for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
909  const std::string &src = link->first;
910  const std::string &dest = link->second;
911  TarAddLink(src, dest, this->subdir);
912  }
913 
914  return true;
915 }
916 
924 bool ExtractTar(const char *tar_filename, Subdirectory subdir)
925 {
926  TarList::iterator it = _tar_list[subdir].find(tar_filename);
927  /* We don't know the file. */
928  if (it == _tar_list[subdir].end()) return false;
929 
930  const char *dirname = (*it).second.dirname;
931 
932  /* The file doesn't have a sub directory! */
933  if (dirname == NULL) return false;
934 
935  char filename[MAX_PATH];
936  strecpy(filename, tar_filename, lastof(filename));
937  char *p = strrchr(filename, PATHSEPCHAR);
938  /* The file's path does not have a separator? */
939  if (p == NULL) return false;
940 
941  p++;
942  strecpy(p, dirname, lastof(filename));
943  DEBUG(misc, 8, "Extracting %s to directory %s", tar_filename, filename);
944  FioCreateDirectory(filename);
945 
946  for (TarFileList::iterator it2 = _tar_filelist[subdir].begin(); it2 != _tar_filelist[subdir].end(); it2++) {
947  if (strcmp((*it2).second.tar_filename, tar_filename) != 0) continue;
948 
949  strecpy(p, (*it2).first.c_str(), lastof(filename));
950 
951  DEBUG(misc, 9, " extracting %s", filename);
952 
953  /* First open the file in the .tar. */
954  size_t to_copy = 0;
955  FILE *in = FioFOpenFileTar(&(*it2).second, &to_copy);
956  if (in == NULL) {
957  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, tar_filename);
958  return false;
959  }
960 
961  /* Now open the 'output' file. */
962  FILE *out = fopen(filename, "wb");
963  if (out == NULL) {
964  DEBUG(misc, 6, "Extracting %s failed; could not open %s", filename, filename);
965  fclose(in);
966  return false;
967  }
968 
969  /* Now read from the tar and write it into the file. */
970  char buffer[4096];
971  size_t read;
972  for (; to_copy != 0; to_copy -= read) {
973  read = fread(buffer, 1, min(to_copy, lengthof(buffer)), in);
974  if (read <= 0 || fwrite(buffer, 1, read, out) != read) break;
975  }
976 
977  /* Close everything up. */
978  fclose(in);
979  fclose(out);
980 
981  if (to_copy != 0) {
982  DEBUG(misc, 6, "Extracting %s failed; still %i bytes to copy", filename, (int)to_copy);
983  return false;
984  }
985  }
986 
987  DEBUG(misc, 9, " extraction successful");
988  return true;
989 }
990 
991 #if defined(WIN32) || defined(WINCE)
992 
997 extern void DetermineBasePaths(const char *exe);
998 #else /* defined(WIN32) || defined(WINCE) */
999 
1007 static bool ChangeWorkingDirectoryToExecutable(const char *exe)
1008 {
1009  char tmp[MAX_PATH];
1010  strecpy(tmp, exe, lastof(tmp));
1011 
1012  bool success = false;
1013 #ifdef WITH_COCOA
1014  char *app_bundle = strchr(tmp, '.');
1015  while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
1016 
1017  if (app_bundle != NULL) *app_bundle = '\0';
1018 #endif /* WITH_COCOA */
1019  char *s = strrchr(tmp, PATHSEPCHAR);
1020  if (s != NULL) {
1021  *s = '\0';
1022 #if defined(__DJGPP__)
1023  /* If we want to go to the root, we can't use cd C:, but we must use '/' */
1024  if (s > tmp && *(s - 1) == ':') chdir("/");
1025 #endif
1026  if (chdir(tmp) != 0) {
1027  DEBUG(misc, 0, "Directory with the binary does not exist?");
1028  } else {
1029  success = true;
1030  }
1031  }
1032  return success;
1033 }
1034 
1046 {
1047  /* No working directory, so nothing to do. */
1048  if (_searchpaths[SP_WORKING_DIR] == NULL) return false;
1049 
1050  /* Working directory is root, so do nothing. */
1051  if (strcmp(_searchpaths[SP_WORKING_DIR], PATHSEP) == 0) return false;
1052 
1053  /* No personal/home directory, so the working directory won't be that. */
1054  if (_searchpaths[SP_PERSONAL_DIR] == NULL) return true;
1055 
1056  char tmp[MAX_PATH];
1057  seprintf(tmp, lastof(tmp), "%s%s", _searchpaths[SP_WORKING_DIR], PERSONAL_DIR);
1058  AppendPathSeparator(tmp, lastof(tmp));
1059  return strcmp(tmp, _searchpaths[SP_PERSONAL_DIR]) != 0;
1060 }
1061 
1066 void DetermineBasePaths(const char *exe)
1067 {
1068  char tmp[MAX_PATH];
1069 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1070  const char *xdg_data_home = xdgDataHome(NULL);
1071  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", xdg_data_home,
1072  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1073  free(xdg_data_home);
1074 
1075  AppendPathSeparator(tmp, lastof(tmp));
1076  _searchpaths[SP_PERSONAL_DIR_XDG] = stredup(tmp);
1077 #endif
1078 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
1079  _searchpaths[SP_PERSONAL_DIR] = NULL;
1080 #else
1081 #ifdef __HAIKU__
1082  BPath path;
1083  find_directory(B_USER_SETTINGS_DIRECTORY, &path);
1084  const char *homedir = stredup(path.Path());
1085 #else
1086  /* getenv is highly unsafe; duplicate it as soon as possible,
1087  * or at least before something else touches the environment
1088  * variables in any way. It can also contain all kinds of
1089  * unvalidated data we rather not want internally. */
1090  const char *homedir = getenv("HOME");
1091  if (homedir != NULL) {
1092  homedir = stredup(homedir);
1093  }
1094 
1095  if (homedir == NULL) {
1096  const struct passwd *pw = getpwuid(getuid());
1097  homedir = (pw == NULL) ? NULL : stredup(pw->pw_dir);
1098  }
1099 #endif
1100 
1101  if (homedir != NULL) {
1102  ValidateString(homedir);
1103  seprintf(tmp, lastof(tmp), "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
1104  AppendPathSeparator(tmp, lastof(tmp));
1105 
1107  free(homedir);
1108  } else {
1109  _searchpaths[SP_PERSONAL_DIR] = NULL;
1110  }
1111 #endif
1112 
1113 #if defined(WITH_SHARED_DIR)
1114  seprintf(tmp, lastof(tmp), "%s", SHARED_DIR);
1115  AppendPathSeparator(tmp, lastof(tmp));
1117 #else
1118  _searchpaths[SP_SHARED_DIR] = NULL;
1119 #endif
1120 
1121 #if defined(__MORPHOS__) || defined(__AMIGA__)
1122  _searchpaths[SP_WORKING_DIR] = NULL;
1123 #else
1124  if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
1125  AppendPathSeparator(tmp, lastof(tmp));
1127 #endif
1128 
1130 
1131  /* Change the working directory to that one of the executable */
1133  if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
1134  AppendPathSeparator(tmp, lastof(tmp));
1136  } else {
1137  _searchpaths[SP_BINARY_DIR] = NULL;
1138  }
1139 
1140  if (_searchpaths[SP_WORKING_DIR] != NULL) {
1141  /* Go back to the current working directory. */
1142  if (chdir(_searchpaths[SP_WORKING_DIR]) != 0) {
1143  DEBUG(misc, 0, "Failed to return to working directory!");
1144  }
1145  }
1146 
1147 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
1149 #else
1150  seprintf(tmp, lastof(tmp), "%s", GLOBAL_DATA_DIR);
1151  AppendPathSeparator(tmp, lastof(tmp));
1153 #endif
1154 #ifdef WITH_COCOA
1155 extern void cocoaSetApplicationBundleDir();
1156  cocoaSetApplicationBundleDir();
1157 #else
1159 #endif
1160 }
1161 #endif /* defined(WIN32) || defined(WINCE) */
1162 
1163 const char *_personal_dir;
1164 
1171 void DeterminePaths(const char *exe)
1172 {
1173  DetermineBasePaths(exe);
1174 
1175 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1176  char config_home[MAX_PATH];
1177 
1178  const char *xdg_config_home = xdgConfigHome(NULL);
1179  seprintf(config_home, lastof(config_home), "%s" PATHSEP "%s", xdg_config_home,
1180  PERSONAL_DIR[0] == '.' ? &PERSONAL_DIR[1] : PERSONAL_DIR);
1181  free(xdg_config_home);
1182 
1183  AppendPathSeparator(config_home, lastof(config_home));
1184 #endif
1185 
1186  Searchpath sp;
1187  FOR_ALL_SEARCHPATHS(sp) {
1188  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1189  DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
1190  }
1191 
1192  char *config_dir;
1193  if (_config_file != NULL) {
1194  config_dir = stredup(_config_file);
1195  char *end = strrchr(config_dir, PATHSEPCHAR);
1196  if (end == NULL) {
1197  config_dir[0] = '\0';
1198  } else {
1199  end[1] = '\0';
1200  }
1201  } else {
1202  char personal_dir[MAX_PATH];
1203  if (FioFindFullPath(personal_dir, lastof(personal_dir), BASE_DIR, "openttd.cfg") != NULL) {
1204  char *end = strrchr(personal_dir, PATHSEPCHAR);
1205  if (end != NULL) end[1] = '\0';
1206  config_dir = stredup(personal_dir);
1207  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1208  } else {
1209 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1210  /* No previous configuration file found. Use the configuration folder from XDG. */
1211  config_dir = config_home;
1212 #else
1213  static const Searchpath new_openttd_cfg_order[] = {
1215  };
1216 
1217  config_dir = NULL;
1218  for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
1219  if (IsValidSearchPath(new_openttd_cfg_order[i])) {
1220  config_dir = stredup(_searchpaths[new_openttd_cfg_order[i]]);
1221  break;
1222  }
1223  }
1224  assert(config_dir != NULL);
1225 #endif
1226  _config_file = str_fmt("%sopenttd.cfg", config_dir);
1227  }
1228  }
1229 
1230  DEBUG(misc, 3, "%s found as config directory", config_dir);
1231 
1232  _highscore_file = str_fmt("%shs.dat", config_dir);
1233  extern char *_hotkeys_file;
1234  _hotkeys_file = str_fmt("%shotkeys.cfg", config_dir);
1235  extern char *_windows_file;
1236  _windows_file = str_fmt("%swindows.cfg", config_dir);
1237 
1238 #if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
1239  if (config_dir == config_home) {
1240  /* We are using the XDG configuration home for the config file,
1241  * then store the rest in the XDG data home folder. */
1242  _personal_dir = _searchpaths[SP_PERSONAL_DIR_XDG];
1243  FioCreateDirectory(_personal_dir);
1244  } else
1245 #endif
1246  {
1247  _personal_dir = config_dir;
1248  }
1249 
1250  /* Make the necessary folders */
1251 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
1252  FioCreateDirectory(config_dir);
1253  if (config_dir != _personal_dir) FioCreateDirectory(_personal_dir);
1254 #endif
1255 
1256  DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
1257 
1258  static const Subdirectory default_subdirs[] = {
1260  };
1261 
1262  for (uint i = 0; i < lengthof(default_subdirs); i++) {
1263  char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
1264  FioCreateDirectory(dir);
1265  free(dir);
1266  }
1267 
1268  /* If we have network we make a directory for the autodownloading of content */
1269  _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
1270 #ifdef ENABLE_NETWORK
1272 
1273  /* Create the directory for each of the types of content */
1275  for (uint i = 0; i < lengthof(dirs); i++) {
1276  char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
1277  FioCreateDirectory(tmp);
1278  free(tmp);
1279  }
1280 
1281  extern char *_log_file;
1282  _log_file = str_fmt("%sopenttd.log", _personal_dir);
1283 #else /* ENABLE_NETWORK */
1284  /* If we don't have networking, we don't need to make the directory. But
1285  * if it exists we keep it, otherwise remove it from the search paths. */
1286  if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
1287  free(_searchpaths[SP_AUTODOWNLOAD_DIR]);
1289  }
1290 #endif /* ENABLE_NETWORK */
1291 }
1292 
1298 {
1299  for (; *filename != '\0'; filename++) {
1300  switch (*filename) {
1301  /* The following characters are not allowed in filenames
1302  * on at least one of the supported operating systems: */
1303  case ':': case '\\': case '*': case '?': case '/':
1304  case '<': case '>': case '|': case '"':
1305  *filename = '_';
1306  break;
1307  }
1308  }
1309 }
1310 
1319 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
1320 {
1321  FILE *in = fopen(filename, "rb");
1322  if (in == NULL) return NULL;
1323 
1324  fseek(in, 0, SEEK_END);
1325  size_t len = ftell(in);
1326  fseek(in, 0, SEEK_SET);
1327  if (len > maxsize) {
1328  fclose(in);
1329  return NULL;
1330  }
1331  byte *mem = MallocT<byte>(len + 1);
1332  mem[len] = 0;
1333  if (fread(mem, len, 1, in) != 1) {
1334  fclose(in);
1335  free(mem);
1336  return NULL;
1337  }
1338  fclose(in);
1339 
1340  *lenp = len;
1341  return mem;
1342 }
1343 
1350 static bool MatchesExtension(const char *extension, const char *filename)
1351 {
1352  if (extension == NULL) return true;
1353 
1354  const char *ext = strrchr(filename, extension[0]);
1355  return ext != NULL && strcasecmp(ext, extension) == 0;
1356 }
1357 
1367 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
1368 {
1369  extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
1370 
1371  uint num = 0;
1372  struct stat sb;
1373  struct dirent *dirent;
1374  DIR *dir;
1375 
1376  if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
1377 
1378  while ((dirent = readdir(dir)) != NULL) {
1379  const char *d_name = FS2OTTD(dirent->d_name);
1380  char filename[MAX_PATH];
1381 
1382  if (!FiosIsValidFile(path, dirent, &sb)) continue;
1383 
1384  seprintf(filename, lastof(filename), "%s%s", path, d_name);
1385 
1386  if (S_ISDIR(sb.st_mode)) {
1387  /* Directory */
1388  if (!recursive) continue;
1389  if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
1390  if (!AppendPathSeparator(filename, lastof(filename))) continue;
1391  num += ScanPath(fs, extension, filename, basepath_length, recursive);
1392  } else if (S_ISREG(sb.st_mode)) {
1393  /* File */
1394  if (MatchesExtension(extension, filename) && fs->AddFile(filename, basepath_length, NULL)) num++;
1395  }
1396  }
1397 
1398  closedir(dir);
1399 
1400  return num;
1401 }
1402 
1409 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
1410 {
1411  uint num = 0;
1412  const char *filename = (*tar).first.c_str();
1413 
1414  if (MatchesExtension(extension, filename) && fs->AddFile(filename, 0, (*tar).second.tar_filename)) num++;
1415 
1416  return num;
1417 }
1418 
1428 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
1429 {
1430  this->subdir = sd;
1431 
1432  Searchpath sp;
1433  char path[MAX_PATH];
1434  TarFileList::iterator tar;
1435  uint num = 0;
1436 
1437  FOR_ALL_SEARCHPATHS(sp) {
1438  /* Don't search in the working directory */
1439  if (sp == SP_WORKING_DIR && !_do_scan_working_directory) continue;
1440 
1441  FioAppendDirectory(path, lastof(path), sp, sd);
1442  num += ScanPath(this, extension, path, strlen(path), recursive);
1443  }
1444 
1445  if (tars && sd != NO_DIRECTORY) {
1446  FOR_ALL_TARS(tar, sd) {
1447  num += ScanTar(this, extension, tar);
1448  }
1449  }
1450 
1451  switch (sd) {
1452  case BASESET_DIR:
1453  num += this->Scan(extension, OLD_GM_DIR, tars, recursive);
1454  FALLTHROUGH;
1455  case NEWGRF_DIR:
1456  num += this->Scan(extension, OLD_DATA_DIR, tars, recursive);
1457  break;
1458 
1459  default: break;
1460  }
1461 
1462  return num;
1463 }
1464 
1473 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
1474 {
1475  char path[MAX_PATH];
1476  strecpy(path, directory, lastof(path));
1477  if (!AppendPathSeparator(path, lastof(path))) return 0;
1478  return ScanPath(this, extension, path, strlen(path), recursive);
1479 }
bool DoScanWorkingDirectory()
Whether we should scan the working directory.
Definition: fileio.cpp:1045
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:139
FILE * cur_fh
current file handle
Definition: fileio.cpp:44
Scan for base sets.
Definition: fileio_func.h:101
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:613
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:342
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:398
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:1428
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:94
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:95
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:1409
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:320
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:73
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:573
Scan for game scripts.
Definition: fileio_func.h:105
Definition: win32.cpp:98
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:694
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:364
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:474
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:1007
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:90
A number of safeguards to prevent using unsafe methods.
Scan for non-base sets.
Definition: fileio_func.h:102
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:630
Scan for scenarios and heightmaps.
Definition: fileio_func.h:104
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:1297
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:126
const char * _personal_dir
custom directory for personal settings, saves, newgrf, etc.
Definition: fileio.cpp:1163
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:1066
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:631
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:1367
uint DoScan(Subdirectory sd)
Perform the scanning of a particular subdirectory.
Definition: fileio.cpp:646
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:39
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:592
Mode
The mode of tar scanning.
Definition: fileio_func.h:99
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:1171
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:1350
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:147
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:453
Scan for AIs and its libraries.
Definition: fileio_func.h:103
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:924
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:1319
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:233
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
static void FioCreateDirectory(const char *name)
Create a directory with the given name.
Definition: fileio.cpp:543
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:87
Old subdirectory for the data.
Definition: fileio_type.h:117