# fios.cpp

Go to the documentation of this file.

    00001 /* $Id: fios.cpp 15710 2009-03-14 15:42:53Z glx $ */
    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 /* WIN32 */
    00019
    00020 #include "table/strings.h"
    00021
    00022 /* Variables to display file lists */
    00023 SmallVector<FiosItem, 32> _fios_items;
    00024 static char *_fios_path;
    00025 SmallFiosItem _file_to_saveload;
    00026
    00027 /* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
    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 /* get the name of an oldstyle savegame */
    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 /* Browse to a new path based on the passed FiosItem struct
    00080  * @param *item FiosItem object telling us what to do
    00081  * @return a string if we have given a file as a target, otherwise NULL */
    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     /* Fallthrough */
    00094     case FIOS_TYPE_INVALID:
    00095       break;
    00096
    00097     case FIOS_TYPE_PARENT: {
    00098       /* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
    00099       char *s = strrchr(path, PATHSEPCHAR);
    00100       if (s != NULL && s != path) {
    00101         s[0] = '\0'; // Remove last path separator character, so we can go up one level.
    00102       }
    00103       s = strrchr(path, PATHSEPCHAR);
    00104       if (s != NULL) s[1] = '\0'; // go up a directory
    00105 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
    00106       /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
    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   /* Don't append the extension if it is already there */
    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   /* There is always one platform that doesn't support basic commands... */
    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   /* virtual */ 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'; // reset the title;
    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   /* If the file doesn't have a title, use it's filename */
    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   /* A parent directory link exists if we are not in the root directory */
    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   /* Show subdirectories */
    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       /* found file must be directory, but not '.' or '..' */
    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   /* Sort the subdirs always by name, ascending, remember user-sorting order */
    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   /* This is where to start sorting for the filenames */
    00306   sort_start = _fios_items.Length();
    00307
    00308   /* Show files */
    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   /* Show drives */
    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   /* Show savegame files
    00361    * .SAV OpenTTD saved game
    00362    * .SS1 Transport Tycoon Deluxe preset game
    00363    * .SV1 Transport Tycoon Deluxe (Patch) saved game
    00364    * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
    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   /* Show scenario files
    00415    * .SCN OpenTTD style scenario file
    00416    * .SV0 Transport Tycoon Deluxe (Patch) scenario
    00417    * .SS0 Transport Tycoon Deluxe preset scenario */
    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   /* Copy the default path on first run or on 'New Game' */
    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   /* Show heightmap files
    00460    * .PNG PNG Based heightmap files
    00461    * .BMP BMP Based heightmap files
    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 /* WITH_PNG */
    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 /* Get a list of Heightmaps */
    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   /* virtual */ 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     /* open the scenario file, but first get the name.
    00553      * This is safe as we check on extension which
    00554      * must always exist. */
    00555     *(char *)strrchr(filename, '.') = '\0';
    00556     f = FioFOpenFile(filename, "rb", SCENARIO_DIR, &size);
    00557     if (f == NULL) return false;
    00558
    00559     /* calculate md5sum */
    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 /* ENABLE_NETWORK */