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