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