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