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 "screenshot.h"
00020 #include "string_func.h"
00021 #include <sys/stat.h>
00022 
00023 #ifndef WIN32
00024 # include <unistd.h>
00025 #endif /* WIN32 */
00026 
00027 #include "table/strings.h"
00028 
00029 /* Variables to display file lists */
00030 SmallVector<FiosItem, 32> _fios_items;
00031 static char *_fios_path;
00032 SmallFiosItem _file_to_saveload;
00033 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
00034 
00035 /* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
00036 extern bool FiosIsRoot(const char *path);
00037 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00038 extern bool FiosIsHiddenFile(const struct dirent *ent);
00039 extern void FiosGetDrives();
00040 extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
00041 
00042 /* get the name of an oldstyle savegame */
00043 extern void GetOldSaveGameName(const char *file, char *title, const char *last);
00044 
00051 int CDECL CompareFiosItems(const FiosItem *da, const FiosItem *db)
00052 {
00053   int r = 0;
00054 
00055   if ((_savegame_sort_order & SORT_BY_NAME) == 0 && da->mtime != db->mtime) {
00056     r = da->mtime < db->mtime ? -1 : 1;
00057   } else {
00058     r = strcasecmp(da->title, db->title);
00059   }
00060 
00061   if (_savegame_sort_order & SORT_DESCENDING) r = -r;
00062   return r;
00063 }
00064 
00066 void FiosFreeSavegameList()
00067 {
00068   _fios_items.Clear();
00069   _fios_items.Compact();
00070 }
00071 
00079 StringID FiosGetDescText(const char **path, uint64 *total_free)
00080 {
00081   *path = _fios_path;
00082   return FiosGetDiskFreeSpace(*path, total_free) ? STR_SAVELOAD_BYTES_FREE : STR_ERROR_UNABLE_TO_READ_DRIVE;
00083 }
00084 
00090 const char *FiosBrowseTo(const FiosItem *item)
00091 {
00092   char *path = _fios_path;
00093 
00094   switch (item->type) {
00095     case FIOS_TYPE_DRIVE:
00096 #if defined(WINCE)
00097       snprintf(path, MAX_PATH, PATHSEP "");
00098 #elif defined(WIN32) || defined(__OS2__)
00099       snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00100 #endif
00101       /* FALL THROUGH */
00102     case FIOS_TYPE_INVALID:
00103       break;
00104 
00105     case FIOS_TYPE_PARENT: {
00106       /* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
00107       char *s = strrchr(path, PATHSEPCHAR);
00108       if (s != NULL && s != path) {
00109         s[0] = '\0'; // Remove last path separator character, so we can go up one level.
00110       }
00111       s = strrchr(path, PATHSEPCHAR);
00112       if (s != NULL) {
00113         s[1] = '\0'; // go up a directory
00114 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00115       /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
00116       } else if ((s = strrchr(path, ':')) != NULL) {
00117         s[1] = '\0';
00118 #endif
00119       }
00120       break;
00121     }
00122 
00123     case FIOS_TYPE_DIR:
00124       strcat(path, item->name);
00125       strcat(path, PATHSEP);
00126       break;
00127 
00128     case FIOS_TYPE_DIRECT:
00129       snprintf(path, MAX_PATH, "%s", item->name);
00130       break;
00131 
00132     case FIOS_TYPE_FILE:
00133     case FIOS_TYPE_OLDFILE:
00134     case FIOS_TYPE_SCENARIO:
00135     case FIOS_TYPE_OLD_SCENARIO:
00136     case FIOS_TYPE_PNG:
00137     case FIOS_TYPE_BMP:
00138       return item->name;
00139   }
00140 
00141   return NULL;
00142 }
00143 
00152 static void FiosMakeFilename(char *buf, const char *path, const char *name, const char *ext, size_t size)
00153 {
00154   const char *period;
00155 
00156   /* Don't append the extension if it is already there */
00157   period = strrchr(name, '.');
00158   if (period != NULL && strcasecmp(period, ext) == 0) ext = "";
00159 #if  defined(__MORPHOS__) || defined(__AMIGAOS__)
00160   if (path != NULL) {
00161     unsigned char sepchar = path[(strlen(path) - 1)];
00162 
00163     if (sepchar != ':' && sepchar != '/') {
00164       snprintf(buf, size, "%s" PATHSEP "%s%s", path, name, ext);
00165     } else {
00166       snprintf(buf, size, "%s%s%s", path, name, ext);
00167     }
00168   } else {
00169     snprintf(buf, size, "%s%s", name, ext);
00170   }
00171 #else
00172   snprintf(buf, size, "%s" PATHSEP "%s%s", path, name, ext);
00173 #endif
00174 }
00175 
00182 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00183 {
00184   const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00185 
00186   FiosMakeFilename(buf, _fios_path, name, extension, size);
00187 }
00188 
00195 void FiosMakeHeightmapName(char *buf, const char *name, size_t size)
00196 {
00197   char ext[5];
00198   ext[0] = '.';
00199   strecpy(ext + 1, GetCurrentScreenshotExtension(), lastof(ext));
00200 
00201   FiosMakeFilename(buf, _fios_path, name, ext, size);
00202 }
00203 
00208 bool FiosDelete(const char *name)
00209 {
00210   char filename[512];
00211 
00212   FiosMakeSavegameName(filename, name, lengthof(filename));
00213   return unlink(filename) == 0;
00214 }
00215 
00216 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00217 
00221 class FiosFileScanner : public FileScanner {
00222   SaveLoadDialogMode mode; 
00223   fios_getlist_callback_proc *callback_proc; 
00224 public:
00230   FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00231     mode(mode),
00232     callback_proc(callback_proc)
00233   {}
00234 
00235   /* virtual */ bool AddFile(const char *filename, size_t basepath_length);
00236 };
00237 
00244 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00245 {
00246   const char *ext = strrchr(filename, '.');
00247   if (ext == NULL) return false;
00248 
00249   char fios_title[64];
00250   fios_title[0] = '\0'; // reset the title;
00251 
00252   FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00253   if (type == FIOS_TYPE_INVALID) return false;
00254 
00255   for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00256     if (strcmp(fios->name, filename) == 0) return false;
00257   }
00258 
00259   FiosItem *fios = _fios_items.Append();
00260 #ifdef WIN32
00261   struct _stat sb;
00262   if (_tstat(OTTD2FS(filename), &sb) == 0) {
00263 #else
00264   struct stat sb;
00265   if (stat(filename, &sb) == 0) {
00266 #endif
00267     fios->mtime = sb.st_mtime;
00268   } else {
00269     fios->mtime = 0;
00270   }
00271 
00272   fios->type = type;
00273   strecpy(fios->name, filename, lastof(fios->name));
00274 
00275   /* If the file doesn't have a title, use its filename */
00276   const char *t = fios_title;
00277   if (StrEmpty(fios_title)) {
00278     t = strrchr(filename, PATHSEPCHAR);
00279     t = (t == NULL) ? filename : (t + 1);
00280   }
00281   strecpy(fios->title, t, lastof(fios->title));
00282   str_validate(fios->title, lastof(fios->title));
00283 
00284   return true;
00285 }
00286 
00287 
00294 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00295 {
00296   struct stat sb;
00297   struct dirent *dirent;
00298   DIR *dir;
00299   FiosItem *fios;
00300   int sort_start;
00301   char d_name[sizeof(fios->name)];
00302 
00303   _fios_items.Clear();
00304 
00305   /* A parent directory link exists if we are not in the root directory */
00306   if (!FiosIsRoot(_fios_path)) {
00307     fios = _fios_items.Append();
00308     fios->type = FIOS_TYPE_PARENT;
00309     fios->mtime = 0;
00310     strecpy(fios->name, "..", lastof(fios->name));
00311     strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00312   }
00313 
00314   /* Show subdirectories */
00315   if ((dir = ttd_opendir(_fios_path)) != NULL) {
00316     while ((dirent = readdir(dir)) != NULL) {
00317       strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00318 
00319       /* found file must be directory, but not '.' or '..' */
00320       if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00321           (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00322           strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00323         fios = _fios_items.Append();
00324         fios->type = FIOS_TYPE_DIR;
00325         fios->mtime = 0;
00326         strecpy(fios->name, d_name, lastof(fios->name));
00327         snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00328         str_validate(fios->title, lastof(fios->title));
00329       }
00330     }
00331     closedir(dir);
00332   }
00333 
00334   /* Sort the subdirs always by name, ascending, remember user-sorting order */
00335   {
00336     SortingBits order = _savegame_sort_order;
00337     _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00338     QSortT(_fios_items.Begin(), _fios_items.Length(), CompareFiosItems);
00339     _savegame_sort_order = order;
00340   }
00341 
00342   /* This is where to start sorting for the filenames */
00343   sort_start = _fios_items.Length();
00344 
00345   /* Show files */
00346   FiosFileScanner scanner(mode, callback_proc);
00347   if (subdir == NO_DIRECTORY) {
00348     scanner.Scan(NULL, _fios_path, false);
00349   } else {
00350     scanner.Scan(NULL, subdir, true, true);
00351   }
00352 
00353   QSortT(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, CompareFiosItems);
00354 
00355   /* Show drives */
00356   FiosGetDrives();
00357 
00358   _fios_items.Compact();
00359 }
00360 
00368 static void GetFileTitle(const char *file, char *title, const char *last)
00369 {
00370   char buf[MAX_PATH];
00371   strecpy(buf, file, lastof(buf));
00372   strecat(buf, ".title", lastof(buf));
00373 
00374   FILE *f = FioFOpenFile(buf, "r");
00375   if (f == NULL) return;
00376 
00377   size_t read = fread(title, 1, last - title, f);
00378   assert(title + read <= last);
00379   title[read] = '\0';
00380   str_validate(title, last);
00381   FioFCloseFile(f);
00382 }
00383 
00395 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00396 {
00397   /* Show savegame files
00398    * .SAV OpenTTD saved game
00399    * .SS1 Transport Tycoon Deluxe preset game
00400    * .SV1 Transport Tycoon Deluxe (Patch) saved game
00401    * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
00402   if (strcasecmp(ext, ".sav") == 0) {
00403     GetFileTitle(file, title, last);
00404     return FIOS_TYPE_FILE;
00405   }
00406 
00407   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00408     if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00409         strcasecmp(ext, ".sv2") == 0) {
00410       if (title != NULL) GetOldSaveGameName(file, title, last);
00411       return FIOS_TYPE_OLDFILE;
00412     }
00413   }
00414 
00415   return FIOS_TYPE_INVALID;
00416 }
00417 
00423 void FiosGetSavegameList(SaveLoadDialogMode mode)
00424 {
00425   static char *fios_save_path = NULL;
00426 
00427   if (fios_save_path == NULL) {
00428     fios_save_path = MallocT<char>(MAX_PATH);
00429     FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00430   }
00431 
00432   _fios_path = fios_save_path;
00433 
00434   FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00435 }
00436 
00448 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00449 {
00450   /* Show scenario files
00451    * .SCN OpenTTD style scenario file
00452    * .SV0 Transport Tycoon Deluxe (Patch) scenario
00453    * .SS0 Transport Tycoon Deluxe preset scenario */
00454   if (strcasecmp(ext, ".scn") == 0) {
00455     GetFileTitle(file, title, last);
00456     return FIOS_TYPE_SCENARIO;
00457   }
00458 
00459   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00460     if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00461       GetOldSaveGameName(file, title, last);
00462       return FIOS_TYPE_OLD_SCENARIO;
00463     }
00464   }
00465 
00466   return FIOS_TYPE_INVALID;
00467 }
00468 
00474 void FiosGetScenarioList(SaveLoadDialogMode mode)
00475 {
00476   static char *fios_scn_path = NULL;
00477 
00478   /* Copy the default path on first run or on 'New Game' */
00479   if (fios_scn_path == NULL) {
00480     fios_scn_path = MallocT<char>(MAX_PATH);
00481     FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00482   }
00483 
00484   _fios_path = fios_scn_path;
00485 
00486   char base_path[MAX_PATH];
00487   FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00488 
00489   FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00490 }
00491 
00492 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00493 {
00494   /* Show heightmap files
00495    * .PNG PNG Based heightmap files
00496    * .BMP BMP Based heightmap files
00497    */
00498 
00499   FiosType type = FIOS_TYPE_INVALID;
00500 
00501 #ifdef WITH_PNG
00502   if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00503 #endif /* WITH_PNG */
00504 
00505   if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00506 
00507   if (type == FIOS_TYPE_INVALID) return FIOS_TYPE_INVALID;
00508 
00509   TarFileList::iterator it = _tar_filelist.find(file);
00510   if (it != _tar_filelist.end()) {
00511     /* If the file is in a tar and that tar is not in a heightmap
00512      * directory we are for sure not supposed to see it.
00513      * Examples of this are pngs part of documentation within
00514      * collections of NewGRFs or 32 bpp graphics replacement PNGs.
00515      */
00516     bool match = false;
00517     Searchpath sp;
00518     FOR_ALL_SEARCHPATHS(sp) {
00519       char buf[MAX_PATH];
00520       FioAppendDirectory(buf, sizeof(buf), sp, HEIGHTMAP_DIR);
00521 
00522       if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
00523         match = true;
00524         break;
00525       }
00526     }
00527 
00528     if (!match) return FIOS_TYPE_INVALID;
00529   }
00530 
00531   GetFileTitle(file, title, last);
00532 
00533   return type;
00534 }
00535 
00540 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00541 {
00542   static char *fios_hmap_path = NULL;
00543 
00544   if (fios_hmap_path == NULL) {
00545     fios_hmap_path = MallocT<char>(MAX_PATH);
00546     FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00547   }
00548 
00549   _fios_path = fios_hmap_path;
00550 
00551   char base_path[MAX_PATH];
00552   FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00553 
00554   FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00555 }
00556 
00557 #if defined(ENABLE_NETWORK)
00558 #include "network/network_content.h"
00559 #include "3rdparty/md5/md5.h"
00560 
00562 struct ScenarioIdentifier {
00563   uint32 scenid;    
00564   uint8 md5sum[16]; 
00565 
00566   bool operator == (const ScenarioIdentifier &other) const
00567   {
00568     return this->scenid == other.scenid &&
00569         memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00570   }
00571 
00572   bool operator != (const ScenarioIdentifier &other) const
00573   {
00574     return !(*this == other);
00575   }
00576 };
00577 
00581 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00582   bool scanned; 
00583 public:
00585   ScenarioScanner() : scanned(false) {}
00586 
00591   void Scan(bool rescan)
00592   {
00593     if (this->scanned && !rescan) return;
00594 
00595     this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00596     this->scanned = true;
00597   }
00598 
00599   /* virtual */ bool AddFile(const char *filename, size_t basepath_length)
00600   {
00601     FILE *f = FioFOpenFile(filename, "r");
00602     if (f == NULL) return false;
00603 
00604     ScenarioIdentifier id;
00605     int fret = fscanf(f, "%i", &id.scenid);
00606     FioFCloseFile(f);
00607     if (fret != 1) return false;
00608 
00609     Md5 checksum;
00610     uint8 buffer[1024];
00611     char basename[MAX_PATH]; 
00612     size_t len, size;
00613 
00614     /* open the scenario file, but first get the name.
00615      * This is safe as we check on extension which
00616      * must always exist. */
00617     strecpy(basename, filename, lastof(basename));
00618     *strrchr(basename, '.') = '\0';
00619     f = FioFOpenFile(basename, "rb", SCENARIO_DIR, &size);
00620     if (f == NULL) return false;
00621 
00622     /* calculate md5sum */
00623     while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00624       size -= len;
00625       checksum.Append(buffer, len);
00626     }
00627     checksum.Finish(id.md5sum);
00628 
00629     FioFCloseFile(f);
00630 
00631     this->Include(id);
00632     return true;
00633   }
00634 };
00635 
00637 static ScenarioScanner _scanner;
00638 
00645 bool HasScenario(const ContentInfo *ci, bool md5sum)
00646 {
00647   _scanner.Scan(false);
00648 
00649   for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00650     if (md5sum ?
00651         (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00652         (id->scenid == ci->unique_id)) {
00653       return true;
00654     }
00655   }
00656 
00657   return false;
00658 }
00659 
00663 void ScanScenarios()
00664 {
00665   _scanner.Scan(true);
00666 }
00667 
00668 #endif /* ENABLE_NETWORK */

Generated on Sun Jun 5 04:19:56 2011 for OpenTTD by  doxygen 1.6.1