00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "fileio_func.h"
00014 #include "variables.h"
00015 #include "debug.h"
00016 #include "fios.h"
00017 #include "string_func.h"
00018 #include "tar_type.h"
00019 #ifdef WIN32
00020 #include <windows.h>
00021 #else
00022 #ifdef OPENBSD
00023 #include <unistd.h>
00024 #endif
00025 #include <pwd.h>
00026 #endif
00027 #include <sys/stat.h>
00028 #include <algorithm>
00029
00030
00031
00032
00033
00034 #define FIO_BUFFER_SIZE 512
00035
00036 struct Fio {
00037 byte *buffer, *buffer_end;
00038 size_t pos;
00039 FILE *cur_fh;
00040 const char *filename;
00041 FILE *handles[MAX_FILE_SLOTS];
00042 byte buffer_start[FIO_BUFFER_SIZE];
00043 const char *filenames[MAX_FILE_SLOTS];
00044 char *shortnames[MAX_FILE_SLOTS];
00045 #if defined(LIMITED_FDS)
00046 uint open_handles;
00047 uint usage_count[MAX_FILE_SLOTS];
00048 #endif
00049 };
00050
00051 static Fio _fio;
00052
00053
00054 size_t FioGetPos()
00055 {
00056 return _fio.pos + (_fio.buffer - _fio.buffer_end);
00057 }
00058
00059 const char *FioGetFilename(uint8 slot)
00060 {
00061 return _fio.shortnames[slot];
00062 }
00063
00064 void FioSeekTo(size_t pos, int mode)
00065 {
00066 if (mode == SEEK_CUR) pos += FioGetPos();
00067 _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00068 _fio.pos = pos;
00069 fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
00070 }
00071
00072 #if defined(LIMITED_FDS)
00073 static void FioRestoreFile(int slot)
00074 {
00075
00076 if (_fio.handles[slot] == NULL) {
00077 DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00078 FioOpenFile(slot, _fio.filenames[slot]);
00079 }
00080 _fio.usage_count[slot]++;
00081 }
00082 #endif
00083
00084
00085 void FioSeekToFile(uint8 slot, size_t pos)
00086 {
00087 FILE *f;
00088 #if defined(LIMITED_FDS)
00089
00090 FioRestoreFile(slot);
00091 #endif
00092 f = _fio.handles[slot];
00093 assert(f != NULL);
00094 _fio.cur_fh = f;
00095 _fio.filename = _fio.filenames[slot];
00096 FioSeekTo(pos, SEEK_SET);
00097 }
00098
00099 byte FioReadByte()
00100 {
00101 if (_fio.buffer == _fio.buffer_end) {
00102 _fio.buffer = _fio.buffer_start;
00103 size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00104 _fio.pos += size;
00105 _fio.buffer_end = _fio.buffer_start + size;
00106
00107 if (size == 0) return 0;
00108 }
00109 return *_fio.buffer++;
00110 }
00111
00112 void FioSkipBytes(int n)
00113 {
00114 for (;;) {
00115 int m = min(_fio.buffer_end - _fio.buffer, n);
00116 _fio.buffer += m;
00117 n -= m;
00118 if (n == 0) break;
00119 FioReadByte();
00120 n--;
00121 }
00122 }
00123
00124 uint16 FioReadWord()
00125 {
00126 byte b = FioReadByte();
00127 return (FioReadByte() << 8) | b;
00128 }
00129
00130 uint32 FioReadDword()
00131 {
00132 uint b = FioReadWord();
00133 return (FioReadWord() << 16) | b;
00134 }
00135
00136 void FioReadBlock(void *ptr, size_t size)
00137 {
00138 FioSeekTo(FioGetPos(), SEEK_SET);
00139 _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00140 }
00141
00142 static inline void FioCloseFile(int slot)
00143 {
00144 if (_fio.handles[slot] != NULL) {
00145 fclose(_fio.handles[slot]);
00146
00147 free(_fio.shortnames[slot]);
00148 _fio.shortnames[slot] = NULL;
00149
00150 _fio.handles[slot] = NULL;
00151 #if defined(LIMITED_FDS)
00152 _fio.open_handles--;
00153 #endif
00154 }
00155 }
00156
00157 void FioCloseAll()
00158 {
00159 int i;
00160
00161 for (i = 0; i != lengthof(_fio.handles); i++)
00162 FioCloseFile(i);
00163 }
00164
00165 #if defined(LIMITED_FDS)
00166 static void FioFreeHandle()
00167 {
00168
00169 if (_fio.open_handles + 1 == LIMITED_FDS) {
00170 uint i, count;
00171 int slot;
00172
00173 count = UINT_MAX;
00174 slot = -1;
00175
00176 for (i = 0; i < lengthof(_fio.handles); i++) {
00177 if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00178 count = _fio.usage_count[i];
00179 slot = i;
00180 }
00181 }
00182 assert(slot != -1);
00183 DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00184 FioCloseFile(slot);
00185 }
00186 }
00187 #endif
00188
00189 void FioOpenFile(int slot, const char *filename)
00190 {
00191 FILE *f;
00192
00193 #if defined(LIMITED_FDS)
00194 FioFreeHandle();
00195 #endif
00196 f = FioFOpenFile(filename);
00197 if (f == NULL) usererror("Cannot open file '%s'", filename);
00198 uint32 pos = ftell(f);
00199
00200 FioCloseFile(slot);
00201 _fio.handles[slot] = f;
00202 _fio.filenames[slot] = filename;
00203
00204
00205 const char *t = strrchr(filename, PATHSEPCHAR);
00206 _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00207 char *t2 = strrchr(_fio.shortnames[slot], '.');
00208 if (t2 != NULL) *t2 = '\0';
00209 strtolower(_fio.shortnames[slot]);
00210
00211 #if defined(LIMITED_FDS)
00212 _fio.usage_count[slot] = 0;
00213 _fio.open_handles++;
00214 #endif
00215 FioSeekToFile(slot, pos);
00216 }
00217
00218 static const char * const _subdirs[NUM_SUBDIRS] = {
00219 "",
00220 "save" PATHSEP,
00221 "save" PATHSEP "autosave" PATHSEP,
00222 "scenario" PATHSEP,
00223 "scenario" PATHSEP "heightmap" PATHSEP,
00224 "gm" PATHSEP,
00225 "data" PATHSEP,
00226 "lang" PATHSEP,
00227 "ai" PATHSEP,
00228 "ai" PATHSEP "library" PATHSEP,
00229 };
00230
00231 const char *_searchpaths[NUM_SEARCHPATHS];
00232 TarList _tar_list;
00233 TarFileList _tar_filelist;
00234
00235 typedef std::map<std::string, std::string> TarLinkList;
00236 static TarLinkList _tar_linklist;
00237
00244 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00245 {
00246 FILE *f = FioFOpenFile(filename, "rb", subdir);
00247 if (f == NULL) return false;
00248
00249 FioFCloseFile(f);
00250 return true;
00251 }
00252
00256 void FioFCloseFile(FILE *f)
00257 {
00258 fclose(f);
00259 }
00260
00261 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00262 {
00263 assert(subdir < NUM_SUBDIRS);
00264 assert(sp < NUM_SEARCHPATHS);
00265
00266 snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00267 return buf;
00268 }
00269
00270 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00271 {
00272 Searchpath sp;
00273 assert(subdir < NUM_SUBDIRS);
00274
00275 FOR_ALL_SEARCHPATHS(sp) {
00276 FioGetFullPath(buf, buflen, sp, subdir, filename);
00277 if (FileExists(buf)) break;
00278 #if !defined(WIN32)
00279
00280
00281
00282 strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1));
00283 if (FileExists(buf)) break;
00284 #endif
00285 }
00286
00287 return buf;
00288 }
00289
00290 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00291 {
00292 assert(subdir < NUM_SUBDIRS);
00293 assert(sp < NUM_SEARCHPATHS);
00294
00295 snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00296 return buf;
00297 }
00298
00299 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00300 {
00301 Searchpath sp;
00302
00303
00304 FOR_ALL_SEARCHPATHS(sp) {
00305 char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00306 if (FileExists(buf)) return ret;
00307 }
00308
00309
00310 ttd_strlcpy(buf, _personal_dir, buflen);
00311
00312 return buf;
00313 }
00314
00315 static FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00316 {
00317 #if defined(WIN32) && defined(UNICODE)
00318
00319
00320
00321
00322 wchar_t Lmode[5];
00323 MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00324 #endif
00325 FILE *f = NULL;
00326 char buf[MAX_PATH];
00327
00328 if (subdir == NO_DIRECTORY) {
00329 strecpy(buf, filename, lastof(buf));
00330 } else {
00331 snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00332 }
00333
00334 #if defined(WIN32)
00335 if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00336 #endif
00337
00338 f = fopen(buf, mode);
00339 #if !defined(WIN32)
00340 if (f == NULL) {
00341 strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1));
00342 f = fopen(buf, mode);
00343 }
00344 #endif
00345 if (f != NULL && filesize != NULL) {
00346
00347 fseek(f, 0, SEEK_END);
00348 *filesize = ftell(f);
00349 fseek(f, 0, SEEK_SET);
00350 }
00351 return f;
00352 }
00353
00354 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00355 {
00356 FILE *f = fopen(entry->tar_filename, "rb");
00357 if (f == NULL) return f;
00358
00359 fseek(f, entry->position, SEEK_SET);
00360 if (filesize != NULL) *filesize = entry->size;
00361 return f;
00362 }
00363
00365 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00366 {
00367 FILE *f = NULL;
00368 Searchpath sp;
00369
00370 assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00371
00372 FOR_ALL_SEARCHPATHS(sp) {
00373 f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00374 if (f != NULL || subdir == NO_DIRECTORY) break;
00375 }
00376
00377
00378 if (f == NULL && mode[0] == 'r') {
00379 static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1;
00380 char resolved_name[MAX_RESOLVED_LENGTH];
00381
00382
00383 strecpy(resolved_name, filename, lastof(resolved_name));
00384 strtolower(resolved_name);
00385
00386 size_t resolved_len = strlen(resolved_name);
00387
00388
00389 for (TarLinkList::iterator link = _tar_linklist.begin(); link != _tar_linklist.end(); link++) {
00390 const std::string &src = link->first;
00391 size_t len = src.length();
00392 if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00393
00394 char resolved_name2[MAX_RESOLVED_LENGTH];
00395 const std::string &dest = link->second;
00396 strecpy(resolved_name2, &(resolved_name[len]), lastof(resolved_name2));
00397 strecpy(resolved_name, dest.c_str(), lastof(resolved_name));
00398 strecpy(&(resolved_name[dest.length()]), resolved_name2, lastof(resolved_name));
00399 break;
00400 }
00401 }
00402
00403 TarFileList::iterator it = _tar_filelist.find(resolved_name);
00404 if (it != _tar_filelist.end()) {
00405 f = FioFOpenFileTar(&((*it).second), filesize);
00406 }
00407 }
00408
00409
00410
00411 if (f == NULL && subdir != NO_DIRECTORY) {
00412 f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00413 }
00414
00415 return f;
00416 }
00417
00422 void FioCreateDirectory(const char *name)
00423 {
00424 #if defined(WIN32) || defined(WINCE)
00425 CreateDirectory(OTTD2FS(name), NULL);
00426 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00427 mkdir(OTTD2FS(name));
00428 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00429 char buf[MAX_PATH];
00430 ttd_strlcpy(buf, name, MAX_PATH);
00431
00432 size_t len = strlen(name) - 1;
00433 if (buf[len] == '/') {
00434 buf[len] = '\0';
00435 }
00436
00437 mkdir(OTTD2FS(buf), 0755);
00438 #else
00439 mkdir(OTTD2FS(name), 0755);
00440 #endif
00441 }
00442
00449 void AppendPathSeparator(char *buf, size_t buflen)
00450 {
00451 size_t s = strlen(buf);
00452
00453
00454 if (s != 0 && buf[s - 1] != PATHSEPCHAR && s + 2 < buflen) {
00455 buf[s] = PATHSEPCHAR;
00456 buf[s + 1] = '\0';
00457 }
00458 }
00459
00466 char *BuildWithFullPath(const char *dir)
00467 {
00468 char *dest = MallocT<char>(MAX_PATH);
00469 ttd_strlcpy(dest, dir, MAX_PATH);
00470
00471
00472 const char *s = strchr(dest, PATHSEPCHAR);
00473
00474
00475 if (s == NULL || dest != s) {
00476 if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00477 AppendPathSeparator(dest, MAX_PATH);
00478 ttd_strlcat(dest, dir, MAX_PATH);
00479 }
00480 AppendPathSeparator(dest, MAX_PATH);
00481
00482 return dest;
00483 }
00484
00485 const char *FioTarFirstDir(const char *tarname)
00486 {
00487 TarList::iterator it = _tar_list.find(tarname);
00488 if (it == _tar_list.end()) return NULL;
00489 return (*it).second.dirname;
00490 }
00491
00492 static void TarAddLink(const std::string &srcParam, const std::string &destParam)
00493 {
00494 std::string src = srcParam;
00495 std::string dest = destParam;
00496
00497 std::transform(src.begin(), src.end(), src.begin(), tolower);
00498 std::transform(dest.begin(), dest.end(), dest.begin(), tolower);
00499
00500 TarFileList::iterator dest_file = _tar_filelist.find(dest);
00501 if (dest_file != _tar_filelist.end()) {
00502
00503 _tar_filelist.insert(TarFileList::value_type(src, dest_file->second));
00504 } else {
00505
00506
00507 const std::string src_path = ((*src.rbegin() == PATHSEPCHAR) ? src : src + PATHSEPCHAR);
00508 const std::string dst_path = (dest.length() == 0 ? "" : ((*dest.rbegin() == PATHSEPCHAR) ? dest : dest + PATHSEPCHAR));
00509 _tar_linklist.insert(TarLinkList::value_type(src_path, dst_path));
00510 }
00511 }
00512
00513 void FioTarAddLink(const char *src, const char *dest)
00514 {
00515 TarAddLink(src, dest);
00516 }
00517
00523 static void SimplifyFileName(char *name)
00524 {
00525
00526 strtolower(name);
00527
00528
00529 #if (PATHSEPCHAR != '/')
00530 for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00531 #endif
00532 }
00533
00534 bool TarListAddFile(const char *filename)
00535 {
00536
00537 typedef struct TarHeader {
00538 char name[100];
00539 char mode[8];
00540 char uid[8];
00541 char gid[8];
00542 char size[12];
00543 char mtime[12];
00544 char chksum[8];
00545 char typeflag;
00546 char linkname[100];
00547 char magic[6];
00548 char version[2];
00549 char uname[32];
00550 char gname[32];
00551 char devmajor[8];
00552 char devminor[8];
00553 char prefix[155];
00554
00555 char unused[12];
00556 } TarHeader;
00557
00558
00559 TarList::iterator it = _tar_list.find(filename);
00560 if (it != _tar_list.end()) return false;
00561
00562 FILE *f = fopen(filename, "rb");
00563 assert(f != NULL);
00564
00565 const char *dupped_filename = strdup(filename);
00566 _tar_list[filename].filename = dupped_filename;
00567 _tar_list[filename].dirname = NULL;
00568
00569 TarLinkList links;
00570
00571 TarHeader th;
00572 char buf[sizeof(th.name) + 1], *end;
00573 char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00574 char link[sizeof(th.linkname) + 1];
00575 char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00576 size_t num = 0, pos = 0;
00577
00578
00579 char empty[512];
00580 memset(&empty[0], 0, sizeof(empty));
00581
00582 for (;;) {
00583 size_t num_bytes_read = fread(&th, 1, 512, f);
00584 if (num_bytes_read != 512) break;
00585 pos += num_bytes_read;
00586
00587
00588 if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00589
00590 if (memcmp(&th, &empty[0], 512) == 0) continue;
00591
00592 DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00593 return false;
00594 }
00595
00596 name[0] = '\0';
00597 size_t len = 0;
00598
00599
00600 if (th.prefix[0] != '\0') {
00601 memcpy(name, th.prefix, sizeof(th.prefix));
00602 name[sizeof(th.prefix)] = '\0';
00603 len = strlen(name);
00604 name[len] = PATHSEPCHAR;
00605 len++;
00606 }
00607
00608
00609 memcpy(&name[len], th.name, sizeof(th.name));
00610 name[len + sizeof(th.name)] = '\0';
00611
00612
00613 memcpy(buf, th.size, sizeof(th.size));
00614 buf[sizeof(th.size)] = '\0';
00615 size_t skip = strtoul(buf, &end, 8);
00616
00617 switch (th.typeflag) {
00618 case '\0':
00619 case '0': {
00620
00621 if (skip == 0) break;
00622
00623 if (strlen(name) == 0) break;
00624
00625
00626 TarFileListEntry entry;
00627 entry.tar_filename = dupped_filename;
00628 entry.size = skip;
00629 entry.position = pos;
00630
00631
00632 SimplifyFileName(name);
00633
00634 DEBUG(misc, 6, "Found file in tar: %s (" PRINTF_SIZE " bytes, " PRINTF_SIZE " offset)", name, skip, pos);
00635 if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
00636
00637 break;
00638 }
00639
00640 case '1':
00641 case '2': {
00642
00643 memcpy(link, th.linkname, sizeof(th.linkname));
00644 link[sizeof(th.linkname)] = '\0';
00645
00646 if (strlen(name) == 0 || strlen(link) == 0) break;
00647
00648
00649 SimplifyFileName(name);
00650 SimplifyFileName(link);
00651
00652
00653 if (link[0] == PATHSEPCHAR) {
00654 DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00655 break;
00656 }
00657
00658
00659
00660 strecpy(dest, name, lastof(dest));
00661 char *destpos = strrchr(dest, PATHSEPCHAR);
00662 if (destpos == NULL) destpos = dest;
00663 *destpos = '\0';
00664
00665 char *pos = link;
00666 while (*pos != '\0') {
00667 char *next = strchr(link, PATHSEPCHAR);
00668 if (next == NULL) next = pos + strlen(pos);
00669
00670
00671 if (next != pos + 1 || pos[0] != '.') {
00672 if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00673
00674 if (dest[0] == '\0') {
00675 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00676 break;
00677 }
00678
00679
00680
00681 destpos = strrchr(dest, PATHSEPCHAR);
00682 if (destpos == NULL) destpos = dest;
00683 } else {
00684
00685 if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00686 strncpy(destpos, pos, next - pos);
00687 destpos += next - pos;
00688 }
00689 *destpos = '\0';
00690 }
00691
00692 pos = next;
00693 }
00694
00695
00696 DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00697 links.insert(TarLinkList::value_type(name, dest));
00698
00699 break;
00700 }
00701
00702 case '5':
00703
00704 SimplifyFileName(name);
00705
00706
00707 DEBUG(misc, 6, "Found dir in tar: %s", name);
00708 if (_tar_list[filename].dirname == NULL) _tar_list[filename].dirname = strdup(name);
00709 break;
00710
00711 default:
00712
00713 break;
00714 }
00715
00716
00717 skip = Align(skip, 512);
00718 fseek(f, skip, SEEK_CUR);
00719 pos += skip;
00720 }
00721
00722 DEBUG(misc, 1, "Found tar '%s' with " PRINTF_SIZE " new files", filename, num);
00723 fclose(f);
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734 for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00735 const std::string &src = link->first;
00736 const std::string &dest = link->second;
00737 TarAddLink(src, dest);
00738 }
00739
00740 return true;
00741 }
00742
00743 static int ScanPathForTarFiles(const char *path, size_t basepath_length)
00744 {
00745 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00746
00747 uint num = 0;
00748 struct stat sb;
00749 struct dirent *dirent;
00750 DIR *dir;
00751
00752 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
00753
00754 while ((dirent = readdir(dir)) != NULL) {
00755 const char *d_name = FS2OTTD(dirent->d_name);
00756 char filename[MAX_PATH];
00757
00758 if (!FiosIsValidFile(path, dirent, &sb)) continue;
00759
00760 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
00761
00762 if (S_ISDIR(sb.st_mode)) {
00763
00764 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
00765 AppendPathSeparator(filename, lengthof(filename));
00766 num += ScanPathForTarFiles(filename, basepath_length);
00767 } else if (S_ISREG(sb.st_mode)) {
00768
00769 char *ext = strrchr(filename, '.');
00770
00771
00772 if (ext == NULL) continue;
00773 if (strcasecmp(ext, ".tar") != 0) continue;
00774
00775 if (TarListAddFile(filename)) num++;
00776 }
00777 }
00778
00779 closedir(dir);
00780 return num;
00781 }
00782
00783 void ScanForTarFiles()
00784 {
00785 Searchpath sp;
00786 char path[MAX_PATH];
00787 uint num = 0;
00788
00789 DEBUG(misc, 1, "Scanning for tars");
00790 FOR_ALL_SEARCHPATHS(sp) {
00791 FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
00792 num += ScanPathForTarFiles(path, strlen(path));
00793 FioAppendDirectory(path, MAX_PATH, sp, AI_DIR);
00794 num += ScanPathForTarFiles(path, strlen(path));
00795 FioAppendDirectory(path, MAX_PATH, sp, AI_LIBRARY_DIR);
00796 num += ScanPathForTarFiles(path, strlen(path));
00797 FioAppendDirectory(path, MAX_PATH, sp, SCENARIO_DIR);
00798 num += ScanPathForTarFiles(path, strlen(path));
00799 }
00800 DEBUG(misc, 1, "Scan complete, found %d files", num);
00801 }
00802
00803 #if defined(WIN32) || defined(WINCE)
00804
00809 extern void DetermineBasePaths(const char *exe);
00810 #else
00811
00819 void ChangeWorkingDirectory(const char *exe)
00820 {
00821 #ifdef WITH_COCOA
00822 char *app_bundle = strchr(exe, '.');
00823 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
00824
00825 if (app_bundle != NULL) app_bundle[0] = '\0';
00826 #endif
00827 char *s = const_cast<char *>(strrchr(exe, PATHSEPCHAR));
00828 if (s != NULL) {
00829 *s = '\0';
00830 #if defined(__DJGPP__)
00831
00832 if (s[-1] == ':') chdir("/");
00833 #endif
00834 if (chdir(exe) != 0) DEBUG(misc, 0, "Directory with the binary does not exist?");
00835 *s = PATHSEPCHAR;
00836 }
00837 #ifdef WITH_COCOA
00838 if (app_bundle != NULL) app_bundle[0] = '.';
00839 #endif
00840 }
00841
00846 void DetermineBasePaths(const char *exe)
00847 {
00848 char tmp[MAX_PATH];
00849 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2) || !defined(WITH_PERSONAL_DIR)
00850 _searchpaths[SP_PERSONAL_DIR] = NULL;
00851 #else
00852 const char *homedir = getenv("HOME");
00853
00854 if (homedir == NULL) {
00855 const struct passwd *pw = getpwuid(getuid());
00856 homedir = (pw == NULL) ? "" : pw->pw_dir;
00857 }
00858
00859 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
00860 AppendPathSeparator(tmp, MAX_PATH);
00861
00862 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00863 #endif
00864
00865 #if defined(WITH_SHARED_DIR)
00866 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
00867 AppendPathSeparator(tmp, MAX_PATH);
00868 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00869 #else
00870 _searchpaths[SP_SHARED_DIR] = NULL;
00871 #endif
00872
00873 #if defined(__MORPHOS__) || defined(__AMIGA__)
00874 _searchpaths[SP_WORKING_DIR] = NULL;
00875 #else
00876 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00877 AppendPathSeparator(tmp, MAX_PATH);
00878 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00879 #endif
00880
00881
00882 ChangeWorkingDirectory(exe);
00883 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00884 AppendPathSeparator(tmp, MAX_PATH);
00885 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00886
00887 if (_searchpaths[SP_WORKING_DIR] != NULL) {
00888
00889 ChangeWorkingDirectory(_searchpaths[SP_WORKING_DIR]);
00890 }
00891
00892 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || defined(OS2)
00893 _searchpaths[SP_INSTALLATION_DIR] = NULL;
00894 #else
00895 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
00896 AppendPathSeparator(tmp, MAX_PATH);
00897 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
00898 #endif
00899 #ifdef WITH_COCOA
00900 extern void cocoaSetApplicationBundleDir();
00901 cocoaSetApplicationBundleDir();
00902 #else
00903 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
00904 #endif
00905 }
00906 #endif
00907
00908 char *_personal_dir;
00909
00916 void DeterminePaths(const char *exe)
00917 {
00918 DetermineBasePaths(exe);
00919
00920 Searchpath sp;
00921 FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
00922
00923 if (_config_file != NULL) {
00924 _personal_dir = strdup(_config_file);
00925 char *end = strrchr(_personal_dir, PATHSEPCHAR);
00926 if (end == NULL) {
00927 _personal_dir[0] = '\0';
00928 } else {
00929 end[1] = '\0';
00930 }
00931 } else {
00932 char personal_dir[MAX_PATH];
00933 FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
00934
00935 if (FileExists(personal_dir)) {
00936 char *end = strrchr(personal_dir, PATHSEPCHAR);
00937 if (end != NULL) end[1] = '\0';
00938 _personal_dir = strdup(personal_dir);
00939 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00940 } else {
00941 static const Searchpath new_openttd_cfg_order[] = {
00942 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
00943 };
00944
00945 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
00946 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
00947 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
00948 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00949 break;
00950 }
00951 }
00952 }
00953 }
00954
00955 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
00956
00957 _highscore_file = str_fmt("%shs.dat", _personal_dir);
00958 _log_file = str_fmt("%sopenttd.log", _personal_dir);
00959
00960
00961 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
00962 FioCreateDirectory(_personal_dir);
00963 #endif
00964
00965 static const Subdirectory default_subdirs[] = {
00966 SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR
00967 };
00968
00969 for (uint i = 0; i < lengthof(default_subdirs); i++) {
00970 char *dir = str_fmt("%s%s", _personal_dir, _subdirs[default_subdirs[i]]);
00971 FioCreateDirectory(dir);
00972 free(dir);
00973 }
00974
00975
00976 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
00977 #ifdef ENABLE_NETWORK
00978 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
00979
00980
00981 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, DATA_DIR, AI_DIR, AI_LIBRARY_DIR, GM_DIR };
00982 for (uint i = 0; i < lengthof(dirs); i++) {
00983 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], _subdirs[dirs[i]]);
00984 FioCreateDirectory(tmp);
00985 free(tmp);
00986 }
00987 #else
00988
00989
00990 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
00991 free((void*)_searchpaths[SP_AUTODOWNLOAD_DIR]);
00992 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
00993 }
00994 #endif
00995
00996 ScanForTarFiles();
00997 }
00998
01003 void SanitizeFilename(char *filename)
01004 {
01005 for (; *filename != '\0'; filename++) {
01006 switch (*filename) {
01007
01008
01009 case ':': case '\\': case '*': case '?': case '/':
01010 case '<': case '>': case '|': case '"':
01011 *filename = '_';
01012 break;
01013 }
01014 }
01015 }
01016
01017 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
01018 {
01019 FILE *in = fopen(filename, "rb");
01020 if (in == NULL) return NULL;
01021
01022 fseek(in, 0, SEEK_END);
01023 size_t len = ftell(in);
01024 fseek(in, 0, SEEK_SET);
01025 if (len > maxsize) {
01026 fclose(in);
01027 return NULL;
01028 }
01029 byte *mem = MallocT<byte>(len + 1);
01030 mem[len] = 0;
01031 if (fread(mem, len, 1, in) != 1) {
01032 fclose(in);
01033 free(mem);
01034 return NULL;
01035 }
01036 fclose(in);
01037
01038 *lenp = len;
01039 return mem;
01040 }
01041
01042
01052 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length, bool recursive)
01053 {
01054 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
01055
01056 uint num = 0;
01057 struct stat sb;
01058 struct dirent *dirent;
01059 DIR *dir;
01060
01061 if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
01062
01063 while ((dirent = readdir(dir)) != NULL) {
01064 const char *d_name = FS2OTTD(dirent->d_name);
01065 char filename[MAX_PATH];
01066
01067 if (!FiosIsValidFile(path, dirent, &sb)) continue;
01068
01069 snprintf(filename, lengthof(filename), "%s%s", path, d_name);
01070
01071 if (S_ISDIR(sb.st_mode)) {
01072
01073 if (!recursive) continue;
01074 if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01075 AppendPathSeparator(filename, lengthof(filename));
01076 num += ScanPath(fs, extension, filename, basepath_length, recursive);
01077 } else if (S_ISREG(sb.st_mode)) {
01078
01079 if (extension != NULL) {
01080 char *ext = strrchr(filename, '.');
01081
01082
01083 if (ext == NULL) continue;
01084 if (strcasecmp(ext, extension) != 0) continue;
01085 }
01086
01087 if (fs->AddFile(filename, basepath_length)) num++;
01088 }
01089 }
01090
01091 closedir(dir);
01092
01093 return num;
01094 }
01095
01102 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01103 {
01104 uint num = 0;
01105 const char *filename = (*tar).first.c_str();
01106
01107 if (extension != NULL) {
01108 const char *ext = strrchr(filename, '.');
01109
01110
01111 if (ext == NULL) return false;
01112 if (strcasecmp(ext, extension) != 0) return false;
01113 }
01114
01115 if (fs->AddFile(filename, 0)) num++;
01116
01117 return num;
01118 }
01119
01129 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars, bool recursive)
01130 {
01131 Searchpath sp;
01132 char path[MAX_PATH];
01133 TarFileList::iterator tar;
01134 uint num = 0;
01135
01136 FOR_ALL_SEARCHPATHS(sp) {
01137 FioAppendDirectory(path, MAX_PATH, sp, sd);
01138 num += ScanPath(this, extension, path, strlen(path), recursive);
01139 }
01140
01141 if (tars) {
01142 FOR_ALL_TARS(tar) {
01143 num += ScanTar(this, extension, tar);
01144 }
01145 }
01146
01147 return num;
01148 }
01149
01158 uint FileScanner::Scan(const char *extension, const char *directory, bool recursive)
01159 {
01160 char path[MAX_PATH];
01161 strecpy(path, directory, lastof(path));
01162 AppendPathSeparator(path, lengthof(path));
01163 return ScanPath(this, extension, path, strlen(path), recursive);
01164 }