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