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