# fios.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    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 /* WIN32 */
    00027
    00028 #include "table/strings.h"
    00029
    00030 /* Variables to display file lists */
    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 /* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
    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 /* get the name of an oldstyle savegame */
    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       /* FALL THROUGH */
    00103     case FIOS_TYPE_INVALID:
    00104       break;
    00105
    00106     case FIOS_TYPE_PARENT: {
    00107       /* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
    00108       char *s = strrchr(path, PATHSEPCHAR);
    00109       if (s != NULL && s != path) {
    00110         s[0] = '\0'; // Remove last path separator character, so we can go up one level.
    00111       }
    00112       s = strrchr(path, PATHSEPCHAR);
    00113       if (s != NULL) {
    00114         s[1] = '\0'; // go up a directory
    00115 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
    00116       /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
    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   /* Don't append the extension if it is already there */
    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   /* There is always one platform that doesn't support basic commands... */
    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   /* virtual */ 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'; // reset the title;
    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   /* If the file doesn't have a title, use its filename */
    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   /* A parent directory link exists if we are not in the root directory */
    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   /* Show subdirectories */
    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       /* found file must be directory, but not '.' or '..' */
    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   /* Sort the subdirs always by name, ascending, remember user-sorting order */
    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   /* This is where to start sorting for the filenames */
    00319   sort_start = _fios_items.Length();
    00320
    00321   /* Show files */
    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   /* Show drives */
    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   /* Show savegame files
    00374    * .SAV OpenTTD saved game
    00375    * .SS1 Transport Tycoon Deluxe preset game
    00376    * .SV1 Transport Tycoon Deluxe (Patch) saved game
    00377    * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
    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   /* Show scenario files
    00428    * .SCN OpenTTD style scenario file
    00429    * .SV0 Transport Tycoon Deluxe (Patch) scenario
    00430    * .SS0 Transport Tycoon Deluxe preset scenario */
    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   /* Copy the default path on first run or on 'New Game' */
    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   /* Show heightmap files
    00473    * .PNG PNG Based heightmap files
    00474    * .BMP BMP Based heightmap files
    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 /* WITH_PNG */
    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     /* If the file is in a tar and that tar is not in a heightmap
    00490      * directory we are for sure not supposed to see it.
    00491      * Examples of this are pngs part of documentation within
    00492      * collections of NewGRFs or 32 bpp graphics replacement PNGs.
    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 /* Get a list of Heightmaps */
    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   /* virtual */ 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     /* open the scenario file, but first get the name.
    00590      * This is safe as we check on extension which
    00591      * must always exist. */
    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     /* calculate md5sum */
    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 /* ENABLE_NETWORK */