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