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