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