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 */

Generated on Sun May 8 07:30:12 2011 for OpenTTD by  doxygen 1.6.1