fios.cpp

Go to the documentation of this file.
00001 /* $Id: fios.cpp 17693 2009-10-04 17:16:41Z rubidium $ */
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 
00014 #include "stdafx.h"
00015 #include "openttd.h"
00016 #include "core/sort_func.hpp"
00017 #include "fios.h"
00018 #include "fileio_func.h"
00019 #include "tar_type.h"
00020 #include "string_func.h"
00021 #include <sys/stat.h>
00022 
00023 #ifdef WIN32
00024 # define access _taccess
00025 #else
00026 # include <unistd.h>
00027 #endif /* WIN32 */
00028 
00029 #include "table/strings.h"
00030 
00031 /* Variables to display file lists */
00032 SmallVector<FiosItem, 32> _fios_items;
00033 static char *_fios_path;
00034 SmallFiosItem _file_to_saveload;
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 
00086 /* Browse to a new path based on the passed FiosItem struct
00087  * @param *item FiosItem object telling us what to do
00088  * @return a string if we have given a file as a target, otherwise NULL */
00089 const char *FiosBrowseTo(const FiosItem *item)
00090 {
00091   char *path = _fios_path;
00092 
00093   switch (item->type) {
00094     case FIOS_TYPE_DRIVE:
00095 #if defined(WINCE)
00096       snprintf(path, MAX_PATH, PATHSEP "");
00097 #elif defined(WIN32) || defined(__OS2__)
00098       snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00099 #endif
00100     /* Fallthrough */
00101     case FIOS_TYPE_INVALID:
00102       break;
00103 
00104     case FIOS_TYPE_PARENT: {
00105       /* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
00106       char *s = strrchr(path, PATHSEPCHAR);
00107       if (s != NULL && s != path) {
00108         s[0] = '\0'; // Remove last path separator character, so we can go up one level.
00109       }
00110       s = strrchr(path, PATHSEPCHAR);
00111       if (s != NULL) {
00112         s[1] = '\0'; // go up a directory
00113 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00114       /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
00115       } else if ((s = strrchr(path, ':')) != NULL) {
00116         s[1] = '\0';
00117 #endif
00118       }
00119       break;
00120     }
00121 
00122     case FIOS_TYPE_DIR:
00123       strcat(path, item->name);
00124       strcat(path, PATHSEP);
00125       break;
00126 
00127     case FIOS_TYPE_DIRECT:
00128       snprintf(path, MAX_PATH, "%s", item->name);
00129       break;
00130 
00131     case FIOS_TYPE_FILE:
00132     case FIOS_TYPE_OLDFILE:
00133     case FIOS_TYPE_SCENARIO:
00134     case FIOS_TYPE_OLD_SCENARIO:
00135     case FIOS_TYPE_PNG:
00136     case FIOS_TYPE_BMP:
00137       return item->name;
00138   }
00139 
00140   return NULL;
00141 }
00142 
00143 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00144 {
00145   const char *extension, *period;
00146 
00147   extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00148 
00149   /* Don't append the extension if it is already there */
00150   period = strrchr(name, '.');
00151   if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
00152 #if  defined(__MORPHOS__) || defined(__AMIGAOS__)
00153   if (_fios_path != NULL) {
00154     unsigned char sepchar = _fios_path[(strlen(_fios_path) - 1)];
00155 
00156     if (sepchar != ':' && sepchar != '/') {
00157       snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00158     } else {
00159       snprintf(buf, size, "%s%s%s", _fios_path, name, extension);
00160     }
00161   } else {
00162     snprintf(buf, size, "%s%s", name, extension);
00163   }
00164 #else
00165   snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00166 #endif
00167 }
00168 
00169 bool FiosDelete(const char *name)
00170 {
00171   char filename[512];
00172 
00173   FiosMakeSavegameName(filename, name, lengthof(filename));
00174   return unlink(filename) == 0;
00175 }
00176 
00177 bool FileExists(const char *filename)
00178 {
00179 #if defined(WINCE)
00180   /* There is always one platform that doesn't support basic commands... */
00181   HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00182   if (hand == INVALID_HANDLE_VALUE) return 1;
00183   CloseHandle(hand);
00184   return 0;
00185 #else
00186   return access(OTTD2FS(filename), 0) == 0;
00187 #endif
00188 }
00189 
00190 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00191 
00195 class FiosFileScanner : public FileScanner {
00196   SaveLoadDialogMode mode; 
00197   fios_getlist_callback_proc *callback_proc; 
00198 public:
00204   FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00205     mode(mode),
00206     callback_proc(callback_proc)
00207   {}
00208 
00209   /* virtual */ bool AddFile(const char *filename, size_t basepath_length);
00210 };
00211 
00218 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00219 {
00220   const char *ext = strrchr(filename, '.');
00221   if (ext == NULL) return false;
00222 
00223   char fios_title[64];
00224   fios_title[0] = '\0'; // reset the title;
00225 
00226   FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00227   if (type == FIOS_TYPE_INVALID) return false;
00228 
00229   for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00230     if (strcmp(fios->name, filename) == 0) return false;
00231   }
00232 
00233   FiosItem *fios = _fios_items.Append();
00234 #ifdef WIN32
00235   struct _stat sb;
00236   if (_tstat(OTTD2FS(filename), &sb) == 0) {
00237 #else
00238   struct stat sb;
00239   if (stat(filename, &sb) == 0) {
00240 #endif
00241     fios->mtime = sb.st_mtime;
00242   } else {
00243     fios->mtime = 0;
00244   }
00245 
00246   fios->type = type;
00247   strecpy(fios->name, filename, lastof(fios->name));
00248 
00249   /* If the file doesn't have a title, use it's filename */
00250   const char *t = fios_title;
00251   if (StrEmpty(fios_title)) {
00252     t = strrchr(filename, PATHSEPCHAR);
00253     t = (t == NULL) ? filename : (t + 1);
00254   }
00255   strecpy(fios->title, t, lastof(fios->title));
00256   str_validate(fios->title, lastof(fios->title));
00257 
00258   return true;
00259 }
00260 
00261 
00267 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00268 {
00269   struct stat sb;
00270   struct dirent *dirent;
00271   DIR *dir;
00272   FiosItem *fios;
00273   int sort_start;
00274   char d_name[sizeof(fios->name)];
00275 
00276   _fios_items.Clear();
00277 
00278   /* A parent directory link exists if we are not in the root directory */
00279   if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
00280     fios = _fios_items.Append();
00281     fios->type = FIOS_TYPE_PARENT;
00282     fios->mtime = 0;
00283     strecpy(fios->name, "..", lastof(fios->name));
00284     strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00285   }
00286 
00287   /* Show subdirectories */
00288   if (mode != SLD_NEW_GAME && (dir = ttd_opendir(_fios_path)) != NULL) {
00289     while ((dirent = readdir(dir)) != NULL) {
00290       strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00291 
00292       /* found file must be directory, but not '.' or '..' */
00293       if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00294           (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00295           strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00296         fios = _fios_items.Append();
00297         fios->type = FIOS_TYPE_DIR;
00298         fios->mtime = 0;
00299         strecpy(fios->name, d_name, lastof(fios->name));
00300         snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00301         str_validate(fios->title, lastof(fios->title));
00302       }
00303     }
00304     closedir(dir);
00305   }
00306 
00307   /* Sort the subdirs always by name, ascending, remember user-sorting order */
00308   {
00309     byte order = _savegame_sort_order;
00310     _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00311     QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems);
00312     _savegame_sort_order = order;
00313   }
00314 
00315   /* This is where to start sorting for the filenames */
00316   sort_start = _fios_items.Length();
00317 
00318   /* Show files */
00319   FiosFileScanner scanner(mode, callback_proc);
00320   if (subdir == NO_DIRECTORY) {
00321     scanner.Scan(NULL, _fios_path, false);
00322   } else {
00323     scanner.Scan(NULL, subdir, true, true);
00324   }
00325 
00326   QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems);
00327 
00328   /* Show drives */
00329   if (mode != SLD_NEW_GAME) FiosGetDrives();
00330 
00331   _fios_items.Compact();
00332 }
00333 
00341 static void GetFileTitle(const char *file, char *title, const char *last)
00342 {
00343   char buf[MAX_PATH];
00344   strecpy(buf, file, lastof(buf));
00345   strecat(buf, ".title", lastof(buf));
00346 
00347   FILE *f = FioFOpenFile(buf, "r");
00348   if (f == NULL) return;
00349 
00350   size_t read = fread(title, 1, last - title, f);
00351   assert(title + read <= last);
00352   title[read] = '\0';
00353   str_validate(title, last);
00354   FioFCloseFile(f);
00355 }
00356 
00368 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00369 {
00370   /* Show savegame files
00371    * .SAV OpenTTD saved game
00372    * .SS1 Transport Tycoon Deluxe preset game
00373    * .SV1 Transport Tycoon Deluxe (Patch) saved game
00374    * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
00375   if (strcasecmp(ext, ".sav") == 0) {
00376     GetFileTitle(file, title, last);
00377     return FIOS_TYPE_FILE;
00378   }
00379 
00380   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00381     if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00382         strcasecmp(ext, ".sv2") == 0) {
00383       if (title != NULL) GetOldSaveGameName(file, title, last);
00384       return FIOS_TYPE_OLDFILE;
00385     }
00386   }
00387 
00388   return FIOS_TYPE_INVALID;
00389 }
00390 
00397 void FiosGetSavegameList(SaveLoadDialogMode mode)
00398 {
00399   static char *fios_save_path = NULL;
00400 
00401   if (fios_save_path == NULL) {
00402     fios_save_path = MallocT<char>(MAX_PATH);
00403     FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00404   }
00405 
00406   _fios_path = fios_save_path;
00407 
00408   FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00409 }
00410 
00422 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00423 {
00424   /* Show scenario files
00425    * .SCN OpenTTD style scenario file
00426    * .SV0 Transport Tycoon Deluxe (Patch) scenario
00427    * .SS0 Transport Tycoon Deluxe preset scenario */
00428   if (strcasecmp(ext, ".scn") == 0) {
00429     GetFileTitle(file, title, last);
00430     return FIOS_TYPE_SCENARIO;
00431   }
00432 
00433   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
00434     if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00435       GetOldSaveGameName(file, title, last);
00436       return FIOS_TYPE_OLD_SCENARIO;
00437     }
00438   }
00439 
00440   return FIOS_TYPE_INVALID;
00441 }
00442 
00449 void FiosGetScenarioList(SaveLoadDialogMode mode)
00450 {
00451   static char *fios_scn_path = NULL;
00452 
00453   /* Copy the default path on first run or on 'New Game' */
00454   if (fios_scn_path == NULL) {
00455     fios_scn_path = MallocT<char>(MAX_PATH);
00456     FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00457   }
00458 
00459   _fios_path = fios_scn_path;
00460 
00461   char base_path[MAX_PATH];
00462   FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00463 
00464   FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00465 }
00466 
00467 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00468 {
00469   /* Show heightmap files
00470    * .PNG PNG Based heightmap files
00471    * .BMP BMP Based heightmap files
00472    */
00473 
00474   FiosType type = FIOS_TYPE_INVALID;
00475 
00476 #ifdef WITH_PNG
00477   if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00478 #endif /* WITH_PNG */
00479 
00480   if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00481 
00482   if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00483 
00484   TarFileList::iterator it = _tar_filelist.find(file);
00485   if (it != _tar_filelist.end()) {
00486     /* If the file is in a tar and that tar is not in a heightmap
00487      * directory we are for sure not supposed to see it.
00488      * Examples of this are pngs part of documentation within
00489      * collections of NewGRFs or 32 bpp graphics replacement PNGs.
00490      */
00491     bool match = false;
00492     Searchpath sp;
00493     FOR_ALL_SEARCHPATHS(sp) {
00494       char buf[MAX_PATH];
00495       FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00496 
00497       if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00498         match = true;
00499         break;
00500       }
00501     }
00502 
00503     if (!match) return FIOS_TYPE_INVALID;
00504   }
00505 
00506   GetFileTitle(file, title, last);
00507 
00508   return type;
00509 }
00510 
00511 /* Get a list of Heightmaps */
00512 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00513 {
00514   static char *fios_hmap_path = NULL;
00515 
00516   if (fios_hmap_path == NULL) {
00517     fios_hmap_path = MallocT<char>(MAX_PATH);
00518     FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00519   }
00520 
00521   _fios_path = fios_hmap_path;
00522 
00523   char base_path[MAX_PATH];
00524   FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00525 
00526   FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00527 }
00528 
00529 #if defined(ENABLE_NETWORK)
00530 #include "network/network_content.h"
00531 #include "3rdparty/md5/md5.h"
00532 
00534 struct ScenarioIdentifier {
00535   uint32 scenid;    
00536   uint8 md5sum[16]; 
00537 
00538   bool operator == (const ScenarioIdentifier &other) const
00539   {
00540     return this->scenid == other.scenid &&
00541         memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00542   }
00543 
00544   bool operator != (const ScenarioIdentifier &other) const
00545   {
00546     return !(*this == other);
00547   }
00548 };
00549 
00553 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00554   bool scanned; 
00555 public:
00557   ScenarioScanner() : scanned(false) {}
00558 
00563   void Scan(bool rescan)
00564   {
00565     if (this->scanned && !rescan) return;
00566 
00567     this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00568     this->scanned = true;
00569   }
00570 
00571   /* virtual */ bool AddFile(const char *filename, size_t basepath_length)
00572   {
00573     FILE *f = FioFOpenFile(filename, "r");
00574     if (f == NULL) return false;
00575 
00576     ScenarioIdentifier id;
00577     int fret = fscanf(f, "%i", &id.scenid);
00578     FioFCloseFile(f);
00579     if (fret != 1) return false;
00580 
00581     Md5 checksum;
00582     uint8 buffer[1024];
00583     char basename[MAX_PATH]; 
00584     size_t len, size;
00585 
00586     /* open the scenario file, but first get the name.
00587      * This is safe as we check on extension which
00588      * must always exist. */
00589     strecpy(basename, filename, lastof(basename));
00590     *strrchr(basename, '.') = '\0';
00591     f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00592     if (f == NULL) return false;
00593 
00594     /* calculate md5sum */
00595     while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00596       size -= len;
00597       checksum.Append(buffer, len);
00598     }
00599     checksum.Finish(id.md5sum);
00600 
00601     FioFCloseFile(f);
00602 
00603     this->Include(id);
00604     return true;
00605   }
00606 };
00607 
00609 static ScenarioScanner _scanner;
00610 
00617 bool HasScenario(const ContentInfo *ci, bool md5sum)
00618 {
00619   _scanner.Scan(false);
00620 
00621   for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00622     if (md5sum ?
00623         (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00624         (id->scenid == ci->unique_id)) {
00625       return true;
00626     }
00627   }
00628 
00629   return false;
00630 }
00631 
00635 void ScanScenarios()
00636 {
00637   _scanner.Scan(true);
00638 }
00639 
00640 #endif /* ENABLE_NETWORK */

Generated on Sat Jun 19 17:14:48 2010 for OpenTTD by  doxygen 1.6.1