00001
00002
00003
00004
00005
00006
00007
00008
00009
00015 #include "stdafx.h"
00016 #include "fios.h"
00017 #include "fileio_func.h"
00018 #include "tar_type.h"
00019 #include "screenshot.h"
00020 #include "string_func.h"
00021 #include <sys/stat.h>
00022
00023 #ifndef WIN32
00024 # include <unistd.h>
00025 #endif
00026
00027 #include "table/strings.h"
00028
00029
00030 SmallVector<FiosItem, 32> _fios_items;
00031 static char *_fios_path;
00032 SmallFiosItem _file_to_saveload;
00033 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
00034
00035
00036 extern bool FiosIsRoot(const char *path);
00037 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00038 extern bool FiosIsHiddenFile(const struct dirent *ent);
00039 extern void FiosGetDrives();
00040 extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
00041
00042
00043 extern void GetOldSaveGameName(const char *file, char *title, const char *last);
00044
00051 int CDECL CompareFiosItems(const FiosItem *da, const FiosItem *db)
00052 {
00053 int r = 0;
00054
00055 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && da->mtime != db->mtime) {
00056 r = da->mtime < db->mtime ? -1 : 1;
00057 } else {
00058 r = strcasecmp(da->title, db->title);
00059 }
00060
00061 if (_savegame_sort_order & SORT_DESCENDING) r = -r;
00062 return r;
00063 }
00064
00066 void FiosFreeSavegameList()
00067 {
00068 _fios_items.Clear();
00069 _fios_items.Compact();
00070 }
00071
00079 StringID FiosGetDescText(const char **path, uint64 *total_free)
00080 {
00081 *path = _fios_path;
00082 return FiosGetDiskFreeSpace(*path, total_free) ? STR_SAVELOAD_BYTES_FREE : STR_ERROR_UNABLE_TO_READ_DRIVE;
00083 }
00084
00090 const char *FiosBrowseTo(const FiosItem *item)
00091 {
00092 char *path = _fios_path;
00093
00094 switch (item->type) {
00095 case FIOS_TYPE_DRIVE:
00096 #if defined(WINCE)
00097 snprintf(path, MAX_PATH, PATHSEP "");
00098 #elif defined(WIN32) || defined(__OS2__)
00099 snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00100 #endif
00101
00102 case FIOS_TYPE_INVALID:
00103 break;
00104
00105 case FIOS_TYPE_PARENT: {
00106
00107 char *s = strrchr(path, PATHSEPCHAR);
00108 if (s != NULL && s != path) {
00109 s[0] = '\0';
00110 }
00111 s = strrchr(path, PATHSEPCHAR);
00112 if (s != NULL) {
00113 s[1] = '\0';
00114 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00115
00116 } else if ((s = strrchr(path, ':')) != NULL) {
00117 s[1] = '\0';
00118 #endif
00119 }
00120 break;
00121 }
00122
00123 case FIOS_TYPE_DIR:
00124 strcat(path, item->name);
00125 strcat(path, PATHSEP);
00126 break;
00127
00128 case FIOS_TYPE_DIRECT:
00129 snprintf(path, MAX_PATH, "%s", item->name);
00130 break;
00131
00132 case FIOS_TYPE_FILE:
00133 case FIOS_TYPE_OLDFILE:
00134 case FIOS_TYPE_SCENARIO:
00135 case FIOS_TYPE_OLD_SCENARIO:
00136 case FIOS_TYPE_PNG:
00137 case FIOS_TYPE_BMP:
00138 return item->name;
00139 }
00140
00141 return NULL;
00142 }
00143
00152 static void FiosMakeFilename(char *buf, const char *path, const char *name, const char *ext, size_t size)
00153 {
00154 const char *period;
00155
00156
00157 period = strrchr(name, '.');
00158 if (period != NULL && strcasecmp(period, ext) == 0) ext = "";
00159 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00160 if (path != NULL) {
00161 unsigned char sepchar = path[(strlen(path) - 1)];
00162
00163 if (sepchar != ':' && sepchar != '/') {
00164 snprintf(buf, size, "%s" PATHSEP "%s%s", path, name, ext);
00165 } else {
00166 snprintf(buf, size, "%s%s%s", path, name, ext);
00167 }
00168 } else {
00169 snprintf(buf, size, "%s%s", name, ext);
00170 }
00171 #else
00172 snprintf(buf, size, "%s" PATHSEP "%s%s", path, name, ext);
00173 #endif
00174 }
00175
00182 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00183 {
00184 const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00185
00186 FiosMakeFilename(buf, _fios_path, name, extension, size);
00187 }
00188
00195 void FiosMakeHeightmapName(char *buf, const char *name, size_t size)
00196 {
00197 char ext[5];
00198 ext[0] = '.';
00199 strecpy(ext + 1, GetCurrentScreenshotExtension(), lastof(ext));
00200
00201 FiosMakeFilename(buf, _fios_path, name, ext, size);
00202 }
00203
00208 bool FiosDelete(const char *name)
00209 {
00210 char filename[512];
00211
00212 FiosMakeSavegameName(filename, name, lengthof(filename));
00213 return unlink(filename) == 0;
00214 }
00215
00216 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00217
00221 class FiosFileScanner : public FileScanner {
00222 SaveLoadDialogMode mode;
00223 fios_getlist_callback_proc *callback_proc;
00224 public:
00230 FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00231 mode(mode),
00232 callback_proc(callback_proc)
00233 {}
00234
00235 bool AddFile(const char *filename, size_t basepath_length);
00236 };
00237
00244 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00245 {
00246 const char *ext = strrchr(filename, '.');
00247 if (ext == NULL) return false;
00248
00249 char fios_title[64];
00250 fios_title[0] = '\0';
00251
00252 FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00253 if (type == FIOS_TYPE_INVALID) return false;
00254
00255 for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00256 if (strcmp(fios->name, filename) == 0) return false;
00257 }
00258
00259 FiosItem *fios = _fios_items.Append();
00260 #ifdef WIN32
00261 struct _stat sb;
00262 if (_tstat(OTTD2FS(filename), &sb) == 0) {
00263 #else
00264 struct stat sb;
00265 if (stat(filename, &sb) == 0) {
00266 #endif
00267 fios->mtime = sb.st_mtime;
00268 } else {
00269 fios->mtime = 0;
00270 }
00271
00272 fios->type = type;
00273 strecpy(fios->name, filename, lastof(fios->name));
00274
00275
00276 const char *t = fios_title;
00277 if (StrEmpty(fios_title)) {
00278 t = strrchr(filename, PATHSEPCHAR);
00279 t = (t == NULL) ? filename : (t + 1);
00280 }
00281 strecpy(fios->title, t, lastof(fios->title));
00282 str_validate(fios->title, lastof(fios->title));
00283
00284 return true;
00285 }
00286
00287
00294 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00295 {
00296 struct stat sb;
00297 struct dirent *dirent;
00298 DIR *dir;
00299 FiosItem *fios;
00300 int sort_start;
00301 char d_name[sizeof(fios->name)];
00302
00303 _fios_items.Clear();
00304
00305
00306 if (!FiosIsRoot(_fios_path)) {
00307 fios = _fios_items.Append();
00308 fios->type = FIOS_TYPE_PARENT;
00309 fios->mtime = 0;
00310 strecpy(fios->name, "..", lastof(fios->name));
00311 strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00312 }
00313
00314
00315 if ((dir = ttd_opendir(_fios_path)) != NULL) {
00316 while ((dirent = readdir(dir)) != NULL) {
00317 strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00318
00319
00320 if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00321 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00322 strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00323 fios = _fios_items.Append();
00324 fios->type = FIOS_TYPE_DIR;
00325 fios->mtime = 0;
00326 strecpy(fios->name, d_name, lastof(fios->name));
00327 snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00328 str_validate(fios->title, lastof(fios->title));
00329 }
00330 }
00331 closedir(dir);
00332 }
00333
00334
00335 {
00336 SortingBits order = _savegame_sort_order;
00337 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00338 QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems);
00339 _savegame_sort_order = order;
00340 }
00341
00342
00343 sort_start = _fios_items.Length();
00344
00345
00346 FiosFileScanner scanner(mode, callback_proc);
00347 if (subdir == NO_DIRECTORY) {
00348 scanner.Scan(NULL, _fios_path, false);
00349 } else {
00350 scanner.Scan(NULL, subdir, true, true);
00351 }
00352
00353 QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems);
00354
00355
00356 FiosGetDrives();
00357
00358 _fios_items.Compact();
00359 }
00360
00368 static void GetFileTitle(const char *file, char *title, const char *last)
00369 {
00370 char buf[MAX_PATH];
00371 strecpy(buf, file, lastof(buf));
00372 strecat(buf, ".title", lastof(buf));
00373
00374 FILE *f = FioFOpenFile(buf, "r");
00375 if (f == NULL) return;
00376
00377 size_t read = fread(title, 1, last - title, f);
00378 assert(title + read <= last);
00379 title[read] = '\0';
00380 str_validate(title, last);
00381 FioFCloseFile(f);
00382 }
00383
00395 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00396 {
00397
00398
00399
00400
00401
00402 if (strcasecmp(ext, ".sav") == 0) {
00403 GetFileTitle(file, title, last);
00404 return FIOS_TYPE_FILE;
00405 }
00406
00407 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00408 if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00409 strcasecmp(ext, ".sv2") == 0) {
00410 if (title != NULL) GetOldSaveGameName(file, title, last);
00411 return FIOS_TYPE_OLDFILE;
00412 }
00413 }
00414
00415 return FIOS_TYPE_INVALID;
00416 }
00417
00423 void FiosGetSavegameList(SaveLoadDialogMode mode)
00424 {
00425 static char *fios_save_path = NULL;
00426
00427 if (fios_save_path == NULL) {
00428 fios_save_path = MallocT<char>(MAX_PATH);
00429 FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00430 }
00431
00432 _fios_path = fios_save_path;
00433
00434 FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00435 }
00436
00448 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00449 {
00450
00451
00452
00453
00454 if (strcasecmp(ext, ".scn") == 0) {
00455 GetFileTitle(file, title, last);
00456 return FIOS_TYPE_SCENARIO;
00457 }
00458
00459 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00460 if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00461 GetOldSaveGameName(file, title, last);
00462 return FIOS_TYPE_OLD_SCENARIO;
00463 }
00464 }
00465
00466 return FIOS_TYPE_INVALID;
00467 }
00468
00474 void FiosGetScenarioList(SaveLoadDialogMode mode)
00475 {
00476 static char *fios_scn_path = NULL;
00477
00478
00479 if (fios_scn_path == NULL) {
00480 fios_scn_path = MallocT<char>(MAX_PATH);
00481 FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00482 }
00483
00484 _fios_path = fios_scn_path;
00485
00486 char base_path[MAX_PATH];
00487 FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00488
00489 FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00490 }
00491
00492 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00493 {
00494
00495
00496
00497
00498
00499 FiosType type = FIOS_TYPE_INVALID;
00500
00501 #ifdef WITH_PNG
00502 if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00503 #endif
00504
00505 if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00506
00507 if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00508
00509 TarFileList::iterator it = _tar_filelist.find(file);
00510 if (it != _tar_filelist.end()) {
00511
00512
00513
00514
00515
00516 bool match = false;
00517 Searchpath sp;
00518 FOR_ALL_SEARCHPATHS(sp) {
00519 char buf[MAX_PATH];
00520 FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00521
00522 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00523 match = true;
00524 break;
00525 }
00526 }
00527
00528 if (!match) return FIOS_TYPE_INVALID;
00529 }
00530
00531 GetFileTitle(file, title, last);
00532
00533 return type;
00534 }
00535
00540 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00541 {
00542 static char *fios_hmap_path = NULL;
00543
00544 if (fios_hmap_path == NULL) {
00545 fios_hmap_path = MallocT<char>(MAX_PATH);
00546 FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00547 }
00548
00549 _fios_path = fios_hmap_path;
00550
00551 char base_path[MAX_PATH];
00552 FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00553
00554 FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00555 }
00556
00557 #if defined(ENABLE_NETWORK)
00558 #include "network/network_content.h"
00559 #include "3rdparty/md5/md5.h"
00560
00562 struct ScenarioIdentifier {
00563 uint32 scenid;
00564 uint8 md5sum[16];
00565
00566 bool operator == (const ScenarioIdentifier &other) const
00567 {
00568 return this->scenid == other.scenid &&
00569 memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00570 }
00571
00572 bool operator != (const ScenarioIdentifier &other) const
00573 {
00574 return !(*this == other);
00575 }
00576 };
00577
00581 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00582 bool scanned;
00583 public:
00585 ScenarioScanner() : scanned(false) {}
00586
00591 void Scan(bool rescan)
00592 {
00593 if (this->scanned && !rescan) return;
00594
00595 this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00596 this->scanned = true;
00597 }
00598
00599 bool AddFile(const char *filename, size_t basepath_length)
00600 {
00601 FILE *f = FioFOpenFile(filename, "r");
00602 if (f == NULL) return false;
00603
00604 ScenarioIdentifier id;
00605 int fret = fscanf(f, "%i", &id.scenid);
00606 FioFCloseFile(f);
00607 if (fret != 1) return false;
00608
00609 Md5 checksum;
00610 uint8 buffer[1024];
00611 char basename[MAX_PATH];
00612 size_t len, size;
00613
00614
00615
00616
00617 strecpy(basename, filename, lastof(basename));
00618 *strrchr(basename, '.') = '\0';
00619 f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00620 if (f == NULL) return false;
00621
00622
00623 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00624 size -= len;
00625 checksum.Append(buffer, len);
00626 }
00627 checksum.Finish(id.md5sum);
00628
00629 FioFCloseFile(f);
00630
00631 this->Include(id);
00632 return true;
00633 }
00634 };
00635
00637 static ScenarioScanner _scanner;
00638
00645 bool HasScenario(const ContentInfo *ci, bool md5sum)
00646 {
00647 _scanner.Scan(false);
00648
00649 for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00650 if (md5sum ?
00651 (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00652 (id->scenid == ci->unique_id)) {
00653 return true;
00654 }
00655 }
00656
00657 return false;
00658 }
00659
00663 void ScanScenarios()
00664 {
00665 _scanner.Scan(true);
00666 }
00667
00668 #endif