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