00001
00002
00007 #include "stdafx.h"
00008 #include "openttd.h"
00009 #include "fios.h"
00010 #include "fileio_func.h"
00011 #include "tar_type.h"
00012 #include "string_func.h"
00013 #include <sys/stat.h>
00014
00015 #ifdef WIN32
00016 # define access _taccess
00017 #else
00018 # include <unistd.h>
00019 #endif
00020
00021 #include "table/strings.h"
00022
00023
00024 SmallVector<FiosItem, 32> _fios_items;
00025 static char *_fios_path;
00026 SmallFiosItem _file_to_saveload;
00027
00028
00029 extern bool FiosIsRoot(const char *path);
00030 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00031 extern bool FiosIsHiddenFile(const struct dirent *ent);
00032 extern void FiosGetDrives();
00033 extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
00034
00035
00036 extern void GetOldSaveGameName(const char *file, char *title, const char *last);
00037
00044 int CDECL compare_FiosItems(const void *a, const void *b)
00045 {
00046 const FiosItem *da = (const FiosItem *)a;
00047 const FiosItem *db = (const FiosItem *)b;
00048 int r = 0;
00049
00050 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && da->mtime != db->mtime) {
00051 r = da->mtime < db->mtime ? -1 : 1;
00052 } else {
00053 r = strcasecmp(da->title, db->title);
00054 }
00055
00056 if (_savegame_sort_order & SORT_DESCENDING) r = -r;
00057 return r;
00058 }
00059
00061 void FiosFreeSavegameList()
00062 {
00063 _fios_items.Clear();
00064 _fios_items.Compact();
00065 };
00066
00074 StringID FiosGetDescText(const char **path, uint64 *total_free)
00075 {
00076 *path = _fios_path;
00077 return FiosGetDiskFreeSpace(*path, total_free) ? STR_4005_BYTES_FREE : STR_4006_UNABLE_TO_READ_DRIVE;
00078 }
00079
00080
00081
00082
00083 const char *FiosBrowseTo(const FiosItem *item)
00084 {
00085 char *path = _fios_path;
00086
00087 switch (item->type) {
00088 case FIOS_TYPE_DRIVE:
00089 #if defined(WINCE)
00090 snprintf(path, MAX_PATH, PATHSEP "");
00091 #elif defined(WIN32) || defined(__OS2__)
00092 snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00093 #endif
00094
00095 case FIOS_TYPE_INVALID:
00096 break;
00097
00098 case FIOS_TYPE_PARENT: {
00099
00100 char *s = strrchr(path, PATHSEPCHAR);
00101 if (s != NULL && s != path) {
00102 s[0] = '\0';
00103 }
00104 s = strrchr(path, PATHSEPCHAR);
00105 if (s != NULL) s[1] = '\0';
00106 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00107
00108 else if ((s = strrchr(path, ':')) != NULL) s[1] = '\0';
00109 #endif
00110 break;
00111 }
00112
00113 case FIOS_TYPE_DIR:
00114 strcat(path, item->name);
00115 strcat(path, PATHSEP);
00116 break;
00117
00118 case FIOS_TYPE_DIRECT:
00119 snprintf(path, MAX_PATH, "%s", item->name);
00120 break;
00121
00122 case FIOS_TYPE_FILE:
00123 case FIOS_TYPE_OLDFILE:
00124 case FIOS_TYPE_SCENARIO:
00125 case FIOS_TYPE_OLD_SCENARIO:
00126 case FIOS_TYPE_PNG:
00127 case FIOS_TYPE_BMP:
00128 return item->name;
00129 }
00130
00131 return NULL;
00132 }
00133
00134 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00135 {
00136 const char *extension, *period;
00137
00138 extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00139
00140
00141 period = strrchr(name, '.');
00142 if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
00143 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00144 if (_fios_path != NULL) {
00145 unsigned char sepchar = _fios_path[(strlen(_fios_path) - 1)];
00146
00147 if (sepchar != ':' && sepchar != '/') {
00148 snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00149 } else {
00150 snprintf(buf, size, "%s%s%s", _fios_path, name, extension);
00151 }
00152 } else {
00153 snprintf(buf, size, "%s%s", name, extension);
00154 }
00155 #else
00156 snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00157 #endif
00158 }
00159
00160 bool FiosDelete(const char *name)
00161 {
00162 char filename[512];
00163
00164 FiosMakeSavegameName(filename, name, lengthof(filename));
00165 return unlink(filename) == 0;
00166 }
00167
00168 bool FileExists(const char *filename)
00169 {
00170 #if defined(WINCE)
00171
00172 HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00173 if (hand == INVALID_HANDLE_VALUE) return 1;
00174 CloseHandle(hand);
00175 return 0;
00176 #else
00177 return access(OTTD2FS(filename), 0) == 0;
00178 #endif
00179 }
00180
00181 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00182
00186 class FiosFileScanner : public FileScanner {
00187 SaveLoadDialogMode mode;
00188 fios_getlist_callback_proc *callback_proc;
00189 public:
00195 FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00196 mode(mode),
00197 callback_proc(callback_proc)
00198 {}
00199
00200 bool AddFile(const char *filename, size_t basepath_length);
00201 };
00202
00209 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00210 {
00211 const char *ext = strrchr(filename, '.');
00212 if (ext == NULL) return false;
00213
00214 char fios_title[64];
00215 fios_title[0] = '\0';
00216
00217 FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00218 if (type == FIOS_TYPE_INVALID) return false;
00219
00220 for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00221 if (strcmp(fios->name, filename) == 0) return false;
00222 }
00223
00224 FiosItem *fios = _fios_items.Append();
00225 #ifdef WIN32
00226 struct _stat sb;
00227 if (_tstat(OTTD2FS(filename), &sb) == 0) {
00228 #else
00229 struct stat sb;
00230 if (stat(filename, &sb) == 0) {
00231 #endif
00232 fios->mtime = sb.st_mtime;
00233 } else {
00234 fios->mtime = 0;
00235 }
00236
00237 fios->type = type;
00238 strecpy(fios->name, filename, lastof(fios->name));
00239
00240
00241 const char *t = fios_title;
00242 if (StrEmpty(fios_title)) {
00243 t = strrchr(filename, PATHSEPCHAR);
00244 t = (t == NULL) ? filename : (t + 1);
00245 }
00246 strecpy(fios->title, t, lastof(fios->title));
00247 str_validate(fios->title, lastof(fios->title));
00248
00249 return true;
00250 }
00251
00252
00258 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00259 {
00260 struct stat sb;
00261 struct dirent *dirent;
00262 DIR *dir;
00263 FiosItem *fios;
00264 int sort_start;
00265 char d_name[sizeof(fios->name)];
00266
00267 _fios_items.Clear();
00268
00269
00270 if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
00271 fios = _fios_items.Append();
00272 fios->type = FIOS_TYPE_PARENT;
00273 fios->mtime = 0;
00274 strecpy(fios->name, "..", lastof(fios->name));
00275 strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00276 }
00277
00278
00279 if (mode != SLD_NEW_GAME && (dir = ttd_opendir(_fios_path)) != NULL) {
00280 while ((dirent = readdir(dir)) != NULL) {
00281 strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00282
00283
00284 if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00285 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00286 strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00287 fios = _fios_items.Append();
00288 fios->type = FIOS_TYPE_DIR;
00289 fios->mtime = 0;
00290 strecpy(fios->name, d_name, lastof(fios->name));
00291 snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00292 str_validate(fios->title, lastof(fios->title));
00293 }
00294 }
00295 closedir(dir);
00296 }
00297
00298
00299 {
00300 byte order = _savegame_sort_order;
00301 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00302 qsort(_fios_items.Begin(), _fios_items.Length(), sizeof(FiosItem), compare_FiosItems);
00303 _savegame_sort_order = order;
00304 }
00305
00306
00307 sort_start = _fios_items.Length();
00308
00309
00310 FiosFileScanner scanner(mode, callback_proc);
00311 if (subdir == NO_DIRECTORY) {
00312 scanner.Scan(NULL, _fios_path, false);
00313 } else {
00314 scanner.Scan(NULL, subdir, true, true);
00315 }
00316
00317 qsort(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, sizeof(FiosItem), compare_FiosItems);
00318
00319
00320 if (mode != SLD_NEW_GAME) FiosGetDrives();
00321
00322 _fios_items.Compact();
00323 }
00324
00332 static void GetFileTitle(const char *file, char *title, const char *last)
00333 {
00334 char buf[MAX_PATH];
00335 strecpy(buf, file, lastof(buf));
00336 strecat(buf, ".title", lastof(buf));
00337
00338 FILE *f = FioFOpenFile(buf, "r");
00339 if (f == NULL) return;
00340
00341 size_t read = fread(title, 1, last - title, f);
00342 assert(title + read <= last);
00343 title[read] = '\0';
00344 str_validate(title, last);
00345 FioFCloseFile(f);
00346 }
00347
00359 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00360 {
00361
00362
00363
00364
00365
00366 if (strcasecmp(ext, ".sav") == 0) {
00367 GetFileTitle(file, title, last);
00368 return FIOS_TYPE_FILE;
00369 }
00370
00371 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00372 if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00373 strcasecmp(ext, ".sv2") == 0) {
00374 if (title != NULL) GetOldSaveGameName(file, title, last);
00375 return FIOS_TYPE_OLDFILE;
00376 }
00377 }
00378
00379 return FIOS_TYPE_INVALID;
00380 }
00381
00388 void FiosGetSavegameList(SaveLoadDialogMode mode)
00389 {
00390 static char *fios_save_path = NULL;
00391
00392 if (fios_save_path == NULL) {
00393 fios_save_path = MallocT<char>(MAX_PATH);
00394 FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00395 }
00396
00397 _fios_path = fios_save_path;
00398
00399 FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00400 }
00401
00413 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00414 {
00415
00416
00417
00418
00419 if (strcasecmp(ext, ".scn") == 0) {
00420 GetFileTitle(file, title, last);
00421 return FIOS_TYPE_SCENARIO;
00422 }
00423
00424 if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
00425 if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00426 GetOldSaveGameName(file, title, last);
00427 return FIOS_TYPE_OLD_SCENARIO;
00428 }
00429 }
00430
00431 return FIOS_TYPE_INVALID;
00432 }
00433
00440 void FiosGetScenarioList(SaveLoadDialogMode mode)
00441 {
00442 static char *fios_scn_path = NULL;
00443
00444
00445 if (fios_scn_path == NULL) {
00446 fios_scn_path = MallocT<char>(MAX_PATH);
00447 FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00448 }
00449
00450 _fios_path = fios_scn_path;
00451
00452 char base_path[MAX_PATH];
00453 FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00454
00455 FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00456 }
00457
00458 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00459 {
00460
00461
00462
00463
00464
00465 FiosType type = FIOS_TYPE_INVALID;
00466
00467 #ifdef WITH_PNG
00468 if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00469 #endif
00470
00471 if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00472
00473 if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00474
00475 TarFileList::iterator it = _tar_filelist.find(file);
00476 if (it != _tar_filelist.end()) {
00477
00478
00479
00480
00481
00482 bool match = false;
00483 Searchpath sp;
00484 FOR_ALL_SEARCHPATHS(sp) {
00485 char buf[MAX_PATH];
00486 FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00487
00488 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00489 match = true;
00490 break;
00491 }
00492 }
00493
00494 if (!match) return FIOS_TYPE_INVALID;
00495 }
00496
00497 GetFileTitle(file, title, last);
00498
00499 return type;
00500 }
00501
00502
00503 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00504 {
00505 static char *fios_hmap_path = NULL;
00506
00507 if (fios_hmap_path == NULL) {
00508 fios_hmap_path = MallocT<char>(MAX_PATH);
00509 FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00510 }
00511
00512 _fios_path = fios_hmap_path;
00513
00514 char base_path[MAX_PATH];
00515 FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00516
00517 FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00518 }
00519
00520 #if defined(ENABLE_NETWORK)
00521 #include "core/smallvec_type.hpp"
00522 #include "network/network_content.h"
00523 #include "md5.h"
00524
00526 struct ScenarioIdentifier {
00527 uint32 scenid;
00528 uint8 md5sum[16];
00529
00530 bool operator == (const ScenarioIdentifier &other) const
00531 {
00532 return this->scenid == other.scenid &&
00533 memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00534 }
00535
00536 bool operator != (const ScenarioIdentifier &other) const
00537 {
00538 return !(*this == other);
00539 }
00540 };
00541
00545 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00546 bool scanned;
00547 public:
00549 ScenarioScanner() : scanned(false) {}
00550
00555 void Scan(bool rescan)
00556 {
00557 if (this->scanned && !rescan) return;
00558
00559 this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00560 this->scanned = true;
00561 }
00562
00563 bool AddFile(const char *filename, size_t basepath_length)
00564 {
00565 FILE *f = FioFOpenFile(filename, "r");
00566 if (f == NULL) return false;
00567
00568 ScenarioIdentifier id;
00569 int fret = fscanf(f, "%i", &id.scenid);
00570 FioFCloseFile(f);
00571 if (fret != 1) return false;
00572
00573 Md5 checksum;
00574 uint8 buffer[1024];
00575 size_t len, size;
00576
00577
00578
00579
00580 *(char *)strrchr(filename, '.') = '\0';
00581 f = FioFOpenFile(filename, "rb", SCENARIO_DIR, &size);
00582 if (f == NULL) return false;
00583
00584
00585 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00586 size -= len;
00587 checksum.Append(buffer, len);
00588 }
00589 checksum.Finish(id.md5sum);
00590
00591 FioFCloseFile(f);
00592
00593 this->Include(id);
00594 return true;
00595 }
00596 };
00597
00599 static ScenarioScanner _scanner;
00600
00607 bool HasScenario(const ContentInfo *ci, bool md5sum)
00608 {
00609 _scanner.Scan(false);
00610
00611 for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00612 if (md5sum ?
00613 (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00614 (id->scenid == ci->unique_id)) {
00615 return true;
00616 }
00617 }
00618
00619 return false;
00620 }
00621
00625 void ScanScenarios()
00626 {
00627 _scanner.Scan(true);
00628 }
00629
00630 #endif