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 if (strcasecmp(ext, ".sav") == 0) {
00404 GetFileTitle(file, title, last, SAVE_DIR);
00405 return FIOS_TYPE_FILE;
00406 }
00407
00408 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00409 if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00410 strcasecmp(ext, ".sv2") == 0) {
00411 if (title != NULL) GetOldSaveGameName(file, title, last);
00412 return FIOS_TYPE_OLDFILE;
00413 }
00414 }
00415
00416 return FIOS_TYPE_INVALID;
00417 }
00418
00424 void FiosGetSavegameList(SaveLoadDialogMode mode)
00425 {
00426 static char *fios_save_path = NULL;
00427
00428 if (fios_save_path == NULL) {
00429 fios_save_path = MallocT<char>(MAX_PATH);
00430 FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00431 }
00432
00433 _fios_path = fios_save_path;
00434
00435 FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00436 }
00437
00449 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00450 {
00451
00452
00453
00454
00455 if (strcasecmp(ext, ".scn") == 0) {
00456 GetFileTitle(file, title, last, SCENARIO_DIR);
00457 return FIOS_TYPE_SCENARIO;
00458 }
00459
00460 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00461 if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00462 GetOldSaveGameName(file, title, last);
00463 return FIOS_TYPE_OLD_SCENARIO;
00464 }
00465 }
00466
00467 return FIOS_TYPE_INVALID;
00468 }
00469
00475 void FiosGetScenarioList(SaveLoadDialogMode mode)
00476 {
00477 static char *fios_scn_path = NULL;
00478
00479
00480 if (fios_scn_path == NULL) {
00481 fios_scn_path = MallocT<char>(MAX_PATH);
00482 FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00483 }
00484
00485 _fios_path = fios_scn_path;
00486
00487 char base_path[MAX_PATH];
00488 FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00489
00490 FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00491 }
00492
00493 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00494 {
00495
00496
00497
00498
00499
00500 FiosType type = FIOS_TYPE_INVALID;
00501
00502 #ifdef WITH_PNG
00503 if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00504 #endif
00505
00506 if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00507
00508 if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00509
00510 TarFileList::iterator it = _tar_filelist[SCENARIO_DIR].find(file);
00511 if (it != _tar_filelist[SCENARIO_DIR].end()) {
00512
00513
00514
00515
00516
00517 bool match = false;
00518 Searchpath sp;
00519 FOR_ALL_SEARCHPATHS(sp) {
00520 char buf[MAX_PATH];
00521 FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00522
00523 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00524 match = true;
00525 break;
00526 }
00527 }
00528
00529 if (!match) return FIOS_TYPE_INVALID;
00530 }
00531
00532 GetFileTitle(file, title, last, HEIGHTMAP_DIR);
00533
00534 return type;
00535 }
00536
00541 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00542 {
00543 static char *fios_hmap_path = NULL;
00544
00545 if (fios_hmap_path == NULL) {
00546 fios_hmap_path = MallocT<char>(MAX_PATH);
00547 FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00548 }
00549
00550 _fios_path = fios_hmap_path;
00551
00552 char base_path[MAX_PATH];
00553 FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00554
00555 FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00556 }
00557
00558 #if defined(ENABLE_NETWORK)
00559 #include "network/network_content.h"
00560 #include "3rdparty/md5/md5.h"
00561
00563 struct ScenarioIdentifier {
00564 uint32 scenid;
00565 uint8 md5sum[16];
00566
00567 bool operator == (const ScenarioIdentifier &other) const
00568 {
00569 return this->scenid == other.scenid &&
00570 memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00571 }
00572
00573 bool operator != (const ScenarioIdentifier &other) const
00574 {
00575 return !(*this == other);
00576 }
00577 };
00578
00582 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00583 bool scanned;
00584 public:
00586 ScenarioScanner() : scanned(false) {}
00587
00592 void Scan(bool rescan)
00593 {
00594 if (this->scanned && !rescan) return;
00595
00596 this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00597 this->scanned = true;
00598 }
00599
00600 bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
00601 {
00602 FILE *f = FioFOpenFile(filename, "r", SCENARIO_DIR);
00603 if (f == NULL) return false;
00604
00605 ScenarioIdentifier id;
00606 int fret = fscanf(f, "%i", &id.scenid);
00607 FioFCloseFile(f);
00608 if (fret != 1) return false;
00609
00610 Md5 checksum;
00611 uint8 buffer[1024];
00612 char basename[MAX_PATH];
00613 size_t len, size;
00614
00615
00616
00617
00618 strecpy(basename, filename, lastof(basename));
00619 *strrchr(basename, '.') = '\0';
00620 f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00621 if (f == NULL) return false;
00622
00623
00624 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00625 size -= len;
00626 checksum.Append(buffer, len);
00627 }
00628 checksum.Finish(id.md5sum);
00629
00630 FioFCloseFile(f);
00631
00632 this->Include(id);
00633 return true;
00634 }
00635 };
00636
00638 static ScenarioScanner _scanner;
00639
00646 bool HasScenario(const ContentInfo *ci, bool md5sum)
00647 {
00648 _scanner.Scan(false);
00649
00650 for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00651 if (md5sum ?
00652 (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00653 (id->scenid == ci->unique_id)) {
00654 return true;
00655 }
00656 }
00657
00658 return false;
00659 }
00660
00664 void ScanScenarios()
00665 {
00666 _scanner.Scan(true);
00667 }
00668
00669 #endif