00001
00002
00003
00004
00005
00006
00007
00008
00009
00015 #include "stdafx.h"
00016 #include "openttd.h"
00017 #include "fios.h"
00018 #include "fileio_func.h"
00019 #include "tar_type.h"
00020 #include "string_func.h"
00021 #include <sys/stat.h>
00022
00023 #ifdef WIN32
00024 # define access _taccess
00025 #else
00026 # include <unistd.h>
00027 #endif
00028
00029 #include "table/strings.h"
00030
00031
00032 SmallVector<FiosItem, 32> _fios_items;
00033 static char *_fios_path;
00034 SmallFiosItem _file_to_saveload;
00035 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
00036
00037
00038 extern bool FiosIsRoot(const char *path);
00039 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00040 extern bool FiosIsHiddenFile(const struct dirent *ent);
00041 extern void FiosGetDrives();
00042 extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
00043
00044
00045 extern void GetOldSaveGameName(const char *file, char *title, const char *last);
00046
00053 int CDECL CompareFiosItems(const FiosItem *da, const FiosItem *db)
00054 {
00055 int r = 0;
00056
00057 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && da->mtime != db->mtime) {
00058 r = da->mtime < db->mtime ? -1 : 1;
00059 } else {
00060 r = strcasecmp(da->title, db->title);
00061 }
00062
00063 if (_savegame_sort_order & SORT_DESCENDING) r = -r;
00064 return r;
00065 }
00066
00068 void FiosFreeSavegameList()
00069 {
00070 _fios_items.Clear();
00071 _fios_items.Compact();
00072 }
00073
00081 StringID FiosGetDescText(const char **path, uint64 *total_free)
00082 {
00083 *path = _fios_path;
00084 return FiosGetDiskFreeSpace(*path, total_free) ? STR_SAVELOAD_BYTES_FREE : STR_ERROR_UNABLE_TO_READ_DRIVE;
00085 }
00086
00092 const char *FiosBrowseTo(const FiosItem *item)
00093 {
00094 char *path = _fios_path;
00095
00096 switch (item->type) {
00097 case FIOS_TYPE_DRIVE:
00098 #if defined(WINCE)
00099 snprintf(path, MAX_PATH, PATHSEP "");
00100 #elif defined(WIN32) || defined(__OS2__)
00101 snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00102 #endif
00103
00104 case FIOS_TYPE_INVALID:
00105 break;
00106
00107 case FIOS_TYPE_PARENT: {
00108
00109 char *s = strrchr(path, PATHSEPCHAR);
00110 if (s != NULL && s != path) {
00111 s[0] = '\0';
00112 }
00113 s = strrchr(path, PATHSEPCHAR);
00114 if (s != NULL) {
00115 s[1] = '\0';
00116 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00117
00118 } else if ((s = strrchr(path, ':')) != NULL) {
00119 s[1] = '\0';
00120 #endif
00121 }
00122 break;
00123 }
00124
00125 case FIOS_TYPE_DIR:
00126 strcat(path, item->name);
00127 strcat(path, PATHSEP);
00128 break;
00129
00130 case FIOS_TYPE_DIRECT:
00131 snprintf(path, MAX_PATH, "%s", item->name);
00132 break;
00133
00134 case FIOS_TYPE_FILE:
00135 case FIOS_TYPE_OLDFILE:
00136 case FIOS_TYPE_SCENARIO:
00137 case FIOS_TYPE_OLD_SCENARIO:
00138 case FIOS_TYPE_PNG:
00139 case FIOS_TYPE_BMP:
00140 return item->name;
00141 }
00142
00143 return NULL;
00144 }
00145
00146 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00147 {
00148 const char *extension, *period;
00149
00150 extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00151
00152
00153 period = strrchr(name, '.');
00154 if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
00155 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00156 if (_fios_path != NULL) {
00157 unsigned char sepchar = _fios_path[(strlen(_fios_path) - 1)];
00158
00159 if (sepchar != ':' && sepchar != '/') {
00160 snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00161 } else {
00162 snprintf(buf, size, "%s%s%s", _fios_path, name, extension);
00163 }
00164 } else {
00165 snprintf(buf, size, "%s%s", name, extension);
00166 }
00167 #else
00168 snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00169 #endif
00170 }
00171
00172 bool FiosDelete(const char *name)
00173 {
00174 char filename[512];
00175
00176 FiosMakeSavegameName(filename, name, lengthof(filename));
00177 return unlink(filename) == 0;
00178 }
00179
00180 bool FileExists(const char *filename)
00181 {
00182 #if defined(WINCE)
00183
00184 HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00185 if (hand == INVALID_HANDLE_VALUE) return 1;
00186 CloseHandle(hand);
00187 return 0;
00188 #else
00189 return access(OTTD2FS(filename), 0) == 0;
00190 #endif
00191 }
00192
00193 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00194
00198 class FiosFileScanner : public FileScanner {
00199 SaveLoadDialogMode mode;
00200 fios_getlist_callback_proc *callback_proc;
00201 public:
00207 FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00208 mode(mode),
00209 callback_proc(callback_proc)
00210 {}
00211
00212 bool AddFile(const char *filename, size_t basepath_length);
00213 };
00214
00221 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00222 {
00223 const char *ext = strrchr(filename, '.');
00224 if (ext == NULL) return false;
00225
00226 char fios_title[64];
00227 fios_title[0] = '\0';
00228
00229 FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00230 if (type == FIOS_TYPE_INVALID) return false;
00231
00232 for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00233 if (strcmp(fios->name, filename) == 0) return false;
00234 }
00235
00236 FiosItem *fios = _fios_items.Append();
00237 #ifdef WIN32
00238 struct _stat sb;
00239 if (_tstat(OTTD2FS(filename), &sb) == 0) {
00240 #else
00241 struct stat sb;
00242 if (stat(filename, &sb) == 0) {
00243 #endif
00244 fios->mtime = sb.st_mtime;
00245 } else {
00246 fios->mtime = 0;
00247 }
00248
00249 fios->type = type;
00250 strecpy(fios->name, filename, lastof(fios->name));
00251
00252
00253 const char *t = fios_title;
00254 if (StrEmpty(fios_title)) {
00255 t = strrchr(filename, PATHSEPCHAR);
00256 t = (t == NULL) ? filename : (t + 1);
00257 }
00258 strecpy(fios->title, t, lastof(fios->title));
00259 str_validate(fios->title, lastof(fios->title));
00260
00261 return true;
00262 }
00263
00264
00271 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00272 {
00273 struct stat sb;
00274 struct dirent *dirent;
00275 DIR *dir;
00276 FiosItem *fios;
00277 int sort_start;
00278 char d_name[sizeof(fios->name)];
00279
00280 _fios_items.Clear();
00281
00282
00283 if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
00284 fios = _fios_items.Append();
00285 fios->type = FIOS_TYPE_PARENT;
00286 fios->mtime = 0;
00287 strecpy(fios->name, "..", lastof(fios->name));
00288 strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00289 }
00290
00291
00292 if (mode != SLD_NEW_GAME && (dir = ttd_opendir(_fios_path)) != NULL) {
00293 while ((dirent = readdir(dir)) != NULL) {
00294 strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00295
00296
00297 if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00298 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00299 strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00300 fios = _fios_items.Append();
00301 fios->type = FIOS_TYPE_DIR;
00302 fios->mtime = 0;
00303 strecpy(fios->name, d_name, lastof(fios->name));
00304 snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00305 str_validate(fios->title, lastof(fios->title));
00306 }
00307 }
00308 closedir(dir);
00309 }
00310
00311
00312 {
00313 SortingBits order = _savegame_sort_order;
00314 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00315 QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems);
00316 _savegame_sort_order = order;
00317 }
00318
00319
00320 sort_start = _fios_items.Length();
00321
00322
00323 FiosFileScanner scanner(mode, callback_proc);
00324 if (subdir == NO_DIRECTORY) {
00325 scanner.Scan(NULL, _fios_path, false);
00326 } else {
00327 scanner.Scan(NULL, subdir, true, true);
00328 }
00329
00330 QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems);
00331
00332
00333 if (mode != SLD_NEW_GAME) FiosGetDrives();
00334
00335 _fios_items.Compact();
00336 }
00337
00345 static void GetFileTitle(const char *file, char *title, const char *last)
00346 {
00347 char buf[MAX_PATH];
00348 strecpy(buf, file, lastof(buf));
00349 strecat(buf, ".title", lastof(buf));
00350
00351 FILE *f = FioFOpenFile(buf, "r");
00352 if (f == NULL) return;
00353
00354 size_t read = fread(title, 1, last - title, f);
00355 assert(title + read <= last);
00356 title[read] = '\0';
00357 str_validate(title, last);
00358 FioFCloseFile(f);
00359 }
00360
00372 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00373 {
00374
00375
00376
00377
00378
00379 if (strcasecmp(ext, ".sav") == 0) {
00380 GetFileTitle(file, title, last);
00381 return FIOS_TYPE_FILE;
00382 }
00383
00384 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00385 if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00386 strcasecmp(ext, ".sv2") == 0) {
00387 if (title != NULL) GetOldSaveGameName(file, title, last);
00388 return FIOS_TYPE_OLDFILE;
00389 }
00390 }
00391
00392 return FIOS_TYPE_INVALID;
00393 }
00394
00401 void FiosGetSavegameList(SaveLoadDialogMode mode)
00402 {
00403 static char *fios_save_path = NULL;
00404
00405 if (fios_save_path == NULL) {
00406 fios_save_path = MallocT<char>(MAX_PATH);
00407 FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00408 }
00409
00410 _fios_path = fios_save_path;
00411
00412 FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00413 }
00414
00426 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00427 {
00428
00429
00430
00431
00432 if (strcasecmp(ext, ".scn") == 0) {
00433 GetFileTitle(file, title, last);
00434 return FIOS_TYPE_SCENARIO;
00435 }
00436
00437 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
00438 if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00439 GetOldSaveGameName(file, title, last);
00440 return FIOS_TYPE_OLD_SCENARIO;
00441 }
00442 }
00443
00444 return FIOS_TYPE_INVALID;
00445 }
00446
00453 void FiosGetScenarioList(SaveLoadDialogMode mode)
00454 {
00455 static char *fios_scn_path = NULL;
00456
00457
00458 if (fios_scn_path == NULL) {
00459 fios_scn_path = MallocT<char>(MAX_PATH);
00460 FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00461 }
00462
00463 _fios_path = fios_scn_path;
00464
00465 char base_path[MAX_PATH];
00466 FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00467
00468 FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00469 }
00470
00471 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00472 {
00473
00474
00475
00476
00477
00478 FiosType type = FIOS_TYPE_INVALID;
00479
00480 #ifdef WITH_PNG
00481 if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00482 #endif
00483
00484 if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00485
00486 if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00487
00488 TarFileList::iterator it = _tar_filelist.find(file);
00489 if (it != _tar_filelist.end()) {
00490
00491
00492
00493
00494
00495 bool match = false;
00496 Searchpath sp;
00497 FOR_ALL_SEARCHPATHS(sp) {
00498 char buf[MAX_PATH];
00499 FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00500
00501 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00502 match = true;
00503 break;
00504 }
00505 }
00506
00507 if (!match) return FIOS_TYPE_INVALID;
00508 }
00509
00510 GetFileTitle(file, title, last);
00511
00512 return type;
00513 }
00514
00515
00516 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00517 {
00518 static char *fios_hmap_path = NULL;
00519
00520 if (fios_hmap_path == NULL) {
00521 fios_hmap_path = MallocT<char>(MAX_PATH);
00522 FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00523 }
00524
00525 _fios_path = fios_hmap_path;
00526
00527 char base_path[MAX_PATH];
00528 FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00529
00530 FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00531 }
00532
00533 #if defined(ENABLE_NETWORK)
00534 #include "network/network_content.h"
00535 #include "3rdparty/md5/md5.h"
00536
00538 struct ScenarioIdentifier {
00539 uint32 scenid;
00540 uint8 md5sum[16];
00541
00542 bool operator == (const ScenarioIdentifier &other) const
00543 {
00544 return this->scenid == other.scenid &&
00545 memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00546 }
00547
00548 bool operator != (const ScenarioIdentifier &other) const
00549 {
00550 return !(*this == other);
00551 }
00552 };
00553
00557 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00558 bool scanned;
00559 public:
00561 ScenarioScanner() : scanned(false) {}
00562
00567 void Scan(bool rescan)
00568 {
00569 if (this->scanned && !rescan) return;
00570
00571 this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00572 this->scanned = true;
00573 }
00574
00575 bool AddFile(const char *filename, size_t basepath_length)
00576 {
00577 FILE *f = FioFOpenFile(filename, "r");
00578 if (f == NULL) return false;
00579
00580 ScenarioIdentifier id;
00581 int fret = fscanf(f, "%i", &id.scenid);
00582 FioFCloseFile(f);
00583 if (fret != 1) return false;
00584
00585 Md5 checksum;
00586 uint8 buffer[1024];
00587 char basename[MAX_PATH];
00588 size_t len, size;
00589
00590
00591
00592
00593 strecpy(basename, filename, lastof(basename));
00594 *strrchr(basename, '.') = '\0';
00595 f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00596 if (f == NULL) return false;
00597
00598
00599 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00600 size -= len;
00601 checksum.Append(buffer, len);
00602 }
00603 checksum.Finish(id.md5sum);
00604
00605 FioFCloseFile(f);
00606
00607 this->Include(id);
00608 return true;
00609 }
00610 };
00611
00613 static ScenarioScanner _scanner;
00614
00621 bool HasScenario(const ContentInfo *ci, bool md5sum)
00622 {
00623 _scanner.Scan(false);
00624
00625 for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00626 if (md5sum ?
00627 (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00628 (id->scenid == ci->unique_id)) {
00629 return true;
00630 }
00631 }
00632
00633 return false;
00634 }
00635
00639 void ScanScenarios()
00640 {
00641 _scanner.Scan(true);
00642 }
00643
00644 #endif