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

Generated on Sun Mar 15 22:49:46 2009 for openttd by  doxygen 1.5.6