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, const char *tar_filename);
00236 };
00237
00244 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
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
00369 static void GetFileTitle(const char *file, char *title, const char *last, Subdirectory subdir)
00370 {
00371 char buf[MAX_PATH];
00372 strecpy(buf, file, lastof(buf));
00373 strecat(buf, ".title", lastof(buf));
00374
00375 FILE *f = FioFOpenFile(buf, "r", subdir);
00376 if (f == NULL) return;
00377
00378 size_t read = fread(title, 1, last - title, f);
00379 assert(title + read <= last);
00380 title[read] = '\0';
00381 str_validate(title, last);
00382 FioFCloseFile(f);
00383 }
00384
00396 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00397 {
00398
00399
00400
00401
00402
00403
00404
00405 if (ext == NULL) return FIOS_TYPE_INVALID;
00406
00407 if (strcasecmp(ext, ".sav") == 0) {
00408 GetFileTitle(file, title, last, SAVE_DIR);
00409 return FIOS_TYPE_FILE;
00410 }
00411
00412 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00413 if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00414 strcasecmp(ext, ".sv2") == 0) {
00415 if (title != NULL) GetOldSaveGameName(file, title, last);
00416 return FIOS_TYPE_OLDFILE;
00417 }
00418 }
00419
00420 return FIOS_TYPE_INVALID;
00421 }
00422
00428 void FiosGetSavegameList(SaveLoadDialogMode mode)
00429 {
00430 static char *fios_save_path = NULL;
00431
00432 if (fios_save_path == NULL) {
00433 fios_save_path = MallocT<char>(MAX_PATH);
00434 FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00435 }
00436
00437 _fios_path = fios_save_path;
00438
00439 FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00440 }
00441
00453 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00454 {
00455
00456
00457
00458
00459 if (strcasecmp(ext, ".scn") == 0) {
00460 GetFileTitle(file, title, last, SCENARIO_DIR);
00461 return FIOS_TYPE_SCENARIO;
00462 }
00463
00464 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00465 if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00466 GetOldSaveGameName(file, title, last);
00467 return FIOS_TYPE_OLD_SCENARIO;
00468 }
00469 }
00470
00471 return FIOS_TYPE_INVALID;
00472 }
00473
00479 void FiosGetScenarioList(SaveLoadDialogMode mode)
00480 {
00481 static char *fios_scn_path = NULL;
00482
00483
00484 if (fios_scn_path == NULL) {
00485 fios_scn_path = MallocT<char>(MAX_PATH);
00486 FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00487 }
00488
00489 _fios_path = fios_scn_path;
00490
00491 char base_path[MAX_PATH];
00492 FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00493
00494 FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00495 }
00496
00497 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00498 {
00499
00500
00501
00502
00503
00504 FiosType type = FIOS_TYPE_INVALID;
00505
00506 #ifdef WITH_PNG
00507 if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00508 #endif
00509
00510 if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00511
00512 if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00513
00514 TarFileList::iterator it = _tar_filelist[SCENARIO_DIR].find(file);
00515 if (it != _tar_filelist[SCENARIO_DIR].end()) {
00516
00517
00518
00519
00520
00521 bool match = false;
00522 Searchpath sp;
00523 FOR_ALL_SEARCHPATHS(sp) {
00524 char buf[MAX_PATH];
00525 FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00526
00527 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00528 match = true;
00529 break;
00530 }
00531 }
00532
00533 if (!match) return FIOS_TYPE_INVALID;
00534 }
00535
00536 GetFileTitle(file, title, last, HEIGHTMAP_DIR);
00537
00538 return type;
00539 }
00540
00545 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00546 {
00547 static char *fios_hmap_path = NULL;
00548
00549 if (fios_hmap_path == NULL) {
00550 fios_hmap_path = MallocT<char>(MAX_PATH);
00551 FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00552 }
00553
00554 _fios_path = fios_hmap_path;
00555
00556 char base_path[MAX_PATH];
00557 FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00558
00559 FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00560 }
00561
00566 const char *FiosGetScreenshotDir()
00567 {
00568 static char *fios_screenshot_path = NULL;
00569
00570 if (fios_screenshot_path == NULL) {
00571 fios_screenshot_path = MallocT<char>(MAX_PATH);
00572 FioGetDirectory(fios_screenshot_path, MAX_PATH, SCREENSHOT_DIR);
00573 }
00574
00575 return fios_screenshot_path;
00576 }
00577
00578 #if defined(ENABLE_NETWORK)
00579 #include "network/network_content.h"
00580 #include "3rdparty/md5/md5.h"
00581
00583 struct ScenarioIdentifier {
00584 uint32 scenid;
00585 uint8 md5sum[16];
00586 char filename[MAX_PATH];
00587
00588 bool operator == (const ScenarioIdentifier &other) const
00589 {
00590 return this->scenid == other.scenid &&
00591 memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00592 }
00593
00594 bool operator != (const ScenarioIdentifier &other) const
00595 {
00596 return !(*this == other);
00597 }
00598 };
00599
00603 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00604 bool scanned;
00605 public:
00607 ScenarioScanner() : scanned(false) {}
00608
00613 void Scan(bool rescan)
00614 {
00615 if (this->scanned && !rescan) return;
00616
00617 this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00618 this->scanned = true;
00619 }
00620
00621 bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00622 {
00623 FILE *f = FioFOpenFile(filename, "r", SCENARIO_DIR);
00624 if (f == NULL) return false;
00625
00626 ScenarioIdentifier id;
00627 int fret = fscanf(f, "%i", &id.scenid);
00628 FioFCloseFile(f);
00629 if (fret != 1) return false;
00630 strecpy(id.filename, filename, lastof(id.filename));
00631
00632 Md5 checksum;
00633 uint8 buffer[1024];
00634 char basename[MAX_PATH];
00635 size_t len, size;
00636
00637
00638
00639
00640 strecpy(basename, filename, lastof(basename));
00641 *strrchr(basename, '.') = '\0';
00642 f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00643 if (f == NULL) return false;
00644
00645
00646 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00647 size -= len;
00648 checksum.Append(buffer, len);
00649 }
00650 checksum.Finish(id.md5sum);
00651
00652 FioFCloseFile(f);
00653
00654 this->Include(id);
00655 return true;
00656 }
00657 };
00658
00660 static ScenarioScanner _scanner;
00661
00668 const char *FindScenario(const ContentInfo *ci, bool md5sum)
00669 {
00670 _scanner.Scan(false);
00671
00672 for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00673 if (md5sum ? (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0)
00674 : (id->scenid == ci->unique_id)) {
00675 return id->filename;
00676 }
00677 }
00678
00679 return NULL;
00680 }
00681
00688 bool HasScenario(const ContentInfo *ci, bool md5sum)
00689 {
00690 return (FindScenario(ci, md5sum) != NULL);
00691 }
00692
00696 void ScanScenarios()
00697 {
00698 _scanner.Scan(true);
00699 }
00700
00701 #endif