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