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 && (subdir == DATA_DIR || subdir == AI_DIR || subdir == AI_LIBRARY_DIR) && 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 }
00781 DEBUG(misc, 1, "Scan complete, found %d files", num);
00782 }
00783
00784 #if defined(WIN32) || defined(WINCE)
00785
00790 extern void DetermineBasePaths(const char *exe);
00791 #else
00792
00800 void ChangeWorkingDirectory(const char *exe)
00801 {
00802 #ifdef WITH_COCOA
00803 char *app_bundle = strchr(exe, '.');
00804 while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
00805
00806 if (app_bundle != NULL) app_bundle[0] = '\0';
00807 #endif
00808 char *s = strrchr(exe, PATHSEPCHAR);
00809 if (s != NULL) {
00810 *s = '\0';
00811 #if defined(__DJGPP__)
00812
00813 if (s[-1] == ':') chdir("/");
00814 #endif
00815 if (chdir(exe) != 0) DEBUG(misc, 0, "Directory with the binary does not exist?");
00816 *s = PATHSEPCHAR;
00817 }
00818 #ifdef WITH_COCOA
00819 if (app_bundle != NULL) app_bundle[0] = '.';
00820 #endif
00821 }
00822
00827 void DetermineBasePaths(const char *exe)
00828 {
00829 char tmp[MAX_PATH];
00830 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || !defined(WITH_PERSONAL_DIR)
00831 _searchpaths[SP_PERSONAL_DIR] = NULL;
00832 #else
00833 const char *homedir = getenv("HOME");
00834
00835 if (homedir == NULL) {
00836 const struct passwd *pw = getpwuid(getuid());
00837 homedir = (pw == NULL) ? "" : pw->pw_dir;
00838 }
00839
00840 snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
00841 AppendPathSeparator(tmp, MAX_PATH);
00842
00843 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00844 #endif
00845
00846 #if defined(WITH_SHARED_DIR)
00847 snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
00848 AppendPathSeparator(tmp, MAX_PATH);
00849 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00850 #else
00851 _searchpaths[SP_SHARED_DIR] = NULL;
00852 #endif
00853
00854 #if defined(__MORPHOS__) || defined(__AMIGA__)
00855 _searchpaths[SP_WORKING_DIR] = NULL;
00856 #else
00857 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00858 AppendPathSeparator(tmp, MAX_PATH);
00859 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00860 #endif
00861
00862
00863 ChangeWorkingDirectory((char*)exe);
00864 if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00865 AppendPathSeparator(tmp, MAX_PATH);
00866 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00867
00868 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS)
00869 _searchpaths[SP_INSTALLATION_DIR] = NULL;
00870 #else
00871 snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
00872 AppendPathSeparator(tmp, MAX_PATH);
00873 _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
00874 #endif
00875 #ifdef WITH_COCOA
00876 extern void cocoaSetApplicationBundleDir();
00877 cocoaSetApplicationBundleDir();
00878 #else
00879 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
00880 #endif
00881 }
00882 #endif
00883
00884 char *_personal_dir;
00885
00892 void DeterminePaths(const char *exe)
00893 {
00894 DetermineBasePaths(exe);
00895
00896 Searchpath sp;
00897 FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
00898
00899 if (_config_file != NULL) {
00900 _personal_dir = strdup(_config_file);
00901 char *end = strrchr(_personal_dir , PATHSEPCHAR);
00902 if (end == NULL) {
00903 _personal_dir[0] = '\0';
00904 } else {
00905 end[1] = '\0';
00906 }
00907 } else {
00908 char personal_dir[MAX_PATH];
00909 FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
00910
00911 if (FileExists(personal_dir)) {
00912 char *end = strrchr(personal_dir, PATHSEPCHAR);
00913 if (end != NULL) end[1] = '\0';
00914 _personal_dir = strdup(personal_dir);
00915 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00916 } else {
00917 static const Searchpath new_openttd_cfg_order[] = {
00918 SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
00919 };
00920
00921 for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
00922 if (IsValidSearchPath(new_openttd_cfg_order[i])) {
00923 _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
00924 _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00925 break;
00926 }
00927 }
00928 }
00929 }
00930
00931 DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
00932
00933 _highscore_file = str_fmt("%shs.dat", _personal_dir);
00934 _log_file = str_fmt("%sopenttd.log", _personal_dir);
00935
00936 char *save_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(SAVE_DIR));
00937 char *autosave_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(AUTOSAVE_DIR));
00938
00939
00940 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
00941 FioCreateDirectory(_personal_dir);
00942 #endif
00943
00944 FioCreateDirectory(save_dir);
00945 FioCreateDirectory(autosave_dir);
00946
00947 free(save_dir);
00948 free(autosave_dir);
00949
00950
00951 _searchpaths[SP_AUTODOWNLOAD_DIR] = str_fmt("%s%s", _personal_dir, "content_download" PATHSEP);
00952 #ifdef ENABLE_NETWORK
00953 FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
00954
00955
00956 const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, DATA_DIR, AI_DIR, AI_LIBRARY_DIR };
00957 for (uint i = 0; i < lengthof(dirs); i++) {
00958 char *tmp = str_fmt("%s%s", _searchpaths[SP_AUTODOWNLOAD_DIR], FioGetSubdirectory(dirs[i]));
00959 FioCreateDirectory(tmp);
00960 free(tmp);
00961 }
00962 #else
00963
00964
00965 if (!FileExists(_searchpaths[SP_AUTODOWNLOAD_DIR])) {
00966 free((void*)_searchpaths[SP_AUTODOWNLOAD_DIR]);
00967 _searchpaths[SP_AUTODOWNLOAD_DIR] = NULL;
00968 }
00969 #endif
00970
00971 ScanForTarFiles();
00972 }
00973
00978 void SanitizeFilename(char *filename)
00979 {
00980 for (; *filename != '\0'; filename++) {
00981 switch (*filename) {
00982
00983
00984 case ':': case '\\': case '*': case '?': case '/':
00985 case '<': case '>': case '|': case '"':
00986 *filename = '_';
00987 break;
00988 }
00989 }
00990 }
00991
00992 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
00993 {
00994 FILE *in;
00995 byte *mem;
00996 size_t len;
00997
00998 in = fopen(filename, "rb");
00999 if (in == NULL) return NULL;
01000
01001 fseek(in, 0, SEEK_END);
01002 len = ftell(in);
01003 fseek(in, 0, SEEK_SET);
01004 if (len > maxsize || (mem = MallocT<byte>(len + 1)) == NULL) {
01005 fclose(in);
01006 return NULL;
01007 }
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)
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 (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
01050 AppendPathSeparator(filename, lengthof(filename));
01051 num += ScanPath(fs, extension, filename, basepath_length);
01052 } else if (S_ISREG(sb.st_mode)) {
01053
01054 char *ext = strrchr(filename, '.');
01055
01056
01057 if (ext == NULL) continue;
01058 if (strcasecmp(ext, extension) != 0) continue;
01059
01060 if (fs->AddFile(filename, basepath_length)) num++;
01061 }
01062 }
01063
01064 closedir(dir);
01065
01066 return num;
01067 }
01068
01074 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01075 {
01076 uint num = 0;
01077 const char *filename = (*tar).first.c_str();
01078 const char *ext = strrchr(filename, '.');
01079
01080
01081 if (ext == NULL) return false;
01082 if (strcasecmp(ext, extension) != 0) return false;
01083
01084 if (fs->AddFile(filename, 0)) num++;
01085
01086 return num;
01087 }
01088
01097 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars)
01098 {
01099 Searchpath sp;
01100 char path[MAX_PATH];
01101 TarFileList::iterator tar;
01102 uint num = 0;
01103
01104 FOR_ALL_SEARCHPATHS(sp) {
01105 FioAppendDirectory(path, MAX_PATH, sp, sd);
01106 num += ScanPath(this, extension, path, strlen(path));
01107 }
01108
01109 if (tars) {
01110 FOR_ALL_TARS(tar) {
01111 num += ScanTar(this, extension, tar);
01112 }
01113 }
01114
01115 return num;
01116 }
01117
01125 uint FileScanner::Scan(const char *extension, const char *directory)
01126 {
01127 char path[MAX_PATH];
01128 strecpy(path, directory, lastof(path));
01129 AppendPathSeparator(path, lengthof(path));
01130 return ScanPath(this, extension, path, strlen(path));
01131 }