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