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