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