fios.cpp

Go to the documentation of this file.
00001 /* $Id: fios.cpp 15657 2009-03-09 21:55:17Z rubidium $ */
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 # include <tchar.h>
00016 # ifndef UNICODE
00017 #  include <io.h>
00018 # endif
00019 # define access _taccess
00020 # define unlink _tunlink
00021 #else
00022 # include <unistd.h>
00023 #endif /* WIN32 */
00024 
00025 #include "table/strings.h"
00026 
00027 /* Variables to display file lists */
00028 SmallVector<FiosItem, 32> _fios_items;
00029 static char *_fios_path;
00030 SmallFiosItem _file_to_saveload;
00031 
00032 /* OS-specific functions are taken from their respective files (win32/unix/os2 .c) */
00033 extern bool FiosIsRoot(const char *path);
00034 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00035 extern bool FiosIsHiddenFile(const struct dirent *ent);
00036 extern void FiosGetDrives();
00037 extern bool FiosGetDiskFreeSpace(const char *path, uint64 *tot);
00038 
00039 /* get the name of an oldstyle savegame */
00040 extern void GetOldSaveGameName(const char *file, char *title, const char *last);
00041 
00048 int CDECL compare_FiosItems(const void *a, const void *b)
00049 {
00050   const FiosItem *da = (const FiosItem *)a;
00051   const FiosItem *db = (const FiosItem *)b;
00052   int r = 0;
00053 
00054   if ((_savegame_sort_order & SORT_BY_NAME) == 0 && da->mtime != db->mtime) {
00055     r = da->mtime < db->mtime ? -1 : 1;
00056   } else {
00057     r = strcasecmp(da->title, db->title);
00058   }
00059 
00060   if (_savegame_sort_order & SORT_DESCENDING) r = -r;
00061   return r;
00062 }
00063 
00065 void FiosFreeSavegameList()
00066 {
00067   _fios_items.Clear();
00068   _fios_items.Compact();
00069 };
00070 
00078 StringID FiosGetDescText(const char **path, uint64 *total_free)
00079 {
00080   *path = _fios_path;
00081   return FiosGetDiskFreeSpace(*path, total_free) ? STR_4005_BYTES_FREE : STR_4006_UNABLE_TO_READ_DRIVE;
00082 }
00083 
00084 /* Browse to a new path based on the passed FiosItem struct
00085  * @param *item FiosItem object telling us what to do
00086  * @return a string if we have given a file as a target, otherwise NULL */
00087 const char *FiosBrowseTo(const FiosItem *item)
00088 {
00089   char *path = _fios_path;
00090 
00091   switch (item->type) {
00092     case FIOS_TYPE_DRIVE:
00093 #if defined(WINCE)
00094       snprintf(path, MAX_PATH, PATHSEP "");
00095 #elif defined(WIN32) || defined(__OS2__)
00096       snprintf(path, MAX_PATH, "%c:" PATHSEP, item->title[0]);
00097 #endif
00098     /* Fallthrough */
00099     case FIOS_TYPE_INVALID:
00100       break;
00101 
00102     case FIOS_TYPE_PARENT: {
00103       /* Check for possible NULL ptr (not required for UNIXes, but AmigaOS-alikes) */
00104       char *s = strrchr(path, PATHSEPCHAR);
00105       if (s != NULL && s != path) {
00106         s[0] = '\0'; // Remove last path separator character, so we can go up one level.
00107       }
00108       s = strrchr(path, PATHSEPCHAR);
00109       if (s != NULL) s[1] = '\0'; // go up a directory
00110 #if defined(__MORPHOS__) || defined(__AMIGAOS__)
00111       /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
00112       else if ((s = strrchr(path, ':')) != NULL) s[1] = '\0';
00113 #endif
00114       break;
00115     }
00116 
00117     case FIOS_TYPE_DIR:
00118       strcat(path, item->name);
00119       strcat(path, PATHSEP);
00120       break;
00121 
00122     case FIOS_TYPE_DIRECT:
00123       snprintf(path, MAX_PATH, "%s", item->name);
00124       break;
00125 
00126     case FIOS_TYPE_FILE:
00127     case FIOS_TYPE_OLDFILE:
00128     case FIOS_TYPE_SCENARIO:
00129     case FIOS_TYPE_OLD_SCENARIO:
00130     case FIOS_TYPE_PNG:
00131     case FIOS_TYPE_BMP:
00132       return item->name;
00133   }
00134 
00135   return NULL;
00136 }
00137 
00138 void FiosMakeSavegameName(char *buf, const char *name, size_t size)
00139 {
00140   const char *extension, *period;
00141 
00142   extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
00143 
00144   /* Don't append the extension if it is already there */
00145   period = strrchr(name, '.');
00146   if (period != NULL && strcasecmp(period, extension) == 0) extension = "";
00147 #if  defined(__MORPHOS__) || defined(__AMIGAOS__)
00148   if (_fios_path != NULL) {
00149     unsigned char sepchar = _fios_path[(strlen(_fios_path) - 1)];
00150 
00151     if (sepchar != ':' && sepchar != '/') {
00152       snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00153     } else {
00154       snprintf(buf, size, "%s%s%s", _fios_path, name, extension);
00155     }
00156   } else {
00157     snprintf(buf, size, "%s%s", name, extension);
00158   }
00159 #else
00160   snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension);
00161 #endif
00162 }
00163 
00164 #if defined(WIN32)
00165 # define unlink _tunlink
00166 #endif
00167 
00168 bool FiosDelete(const char *name)
00169 {
00170   char filename[512];
00171 
00172   FiosMakeSavegameName(filename, name, lengthof(filename));
00173   return unlink(OTTD2FS(filename)) == 0;
00174 }
00175 
00176 bool FileExists(const char *filename)
00177 {
00178 #if defined(WINCE)
00179   /* There is always one platform that doesn't support basic commands... */
00180   HANDLE hand = CreateFile(OTTD2FS(filename), 0, 0, NULL, OPEN_EXISTING, 0, NULL);
00181   if (hand == INVALID_HANDLE_VALUE) return 1;
00182   CloseHandle(hand);
00183   return 0;
00184 #else
00185   return access(OTTD2FS(filename), 0) == 0;
00186 #endif
00187 }
00188 
00189 typedef FiosType fios_getlist_callback_proc(SaveLoadDialogMode mode, const char *filename, const char *ext, char *title, const char *last);
00190 
00194 class FiosFileScanner : public FileScanner {
00195   SaveLoadDialogMode mode; 
00196   fios_getlist_callback_proc *callback_proc; 
00197 public:
00203   FiosFileScanner(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc) :
00204     mode(mode),
00205     callback_proc(callback_proc)
00206   {}
00207 
00208   /* virtual */ bool AddFile(const char *filename, size_t basepath_length);
00209 };
00210 
00217 bool FiosFileScanner::AddFile(const char *filename, size_t basepath_length)
00218 {
00219   const char *ext = strrchr(filename, '.');
00220   if (ext == NULL) return false;
00221 
00222   char fios_title[64];
00223   fios_title[0] = '\0'; // reset the title;
00224 
00225   FiosType type = this->callback_proc(this->mode, filename, ext, fios_title, lastof(fios_title));
00226   if (type == FIOS_TYPE_INVALID) return false;
00227 
00228   for (const FiosItem *fios = _fios_items.Begin(); fios != _fios_items.End(); fios++) {
00229     if (strcmp(fios->name, filename) == 0) return false;
00230   }
00231 
00232   FiosItem *fios = _fios_items.Append();
00233   struct stat sb;
00234   if (stat(filename, &sb) == 0) {
00235     fios->mtime = sb.st_mtime;
00236   } else {
00237     fios->mtime = 0;
00238   }
00239 
00240   fios->type = type;
00241   strecpy(fios->name, filename, lastof(fios->name));
00242 
00243   /* If the file doesn't have a title, use it's filename */
00244   const char *t = fios_title;
00245   if (StrEmpty(fios_title)) {
00246     t = strrchr(filename, PATHSEPCHAR);
00247     t = (t == NULL) ? filename : (t + 1);
00248   }
00249   strecpy(fios->title, t, lastof(fios->title));
00250   str_validate(fios->title, lastof(fios->title));
00251 
00252   return true;
00253 }
00254 
00255 
00261 static void FiosGetFileList(SaveLoadDialogMode mode, fios_getlist_callback_proc *callback_proc, Subdirectory subdir)
00262 {
00263   struct stat sb;
00264   struct dirent *dirent;
00265   DIR *dir;
00266   FiosItem *fios;
00267   int sort_start;
00268   char d_name[sizeof(fios->name)];
00269 
00270   _fios_items.Clear();
00271 
00272   /* A parent directory link exists if we are not in the root directory */
00273   if (!FiosIsRoot(_fios_path) && mode != SLD_NEW_GAME) {
00274     fios = _fios_items.Append();
00275     fios->type = FIOS_TYPE_PARENT;
00276     fios->mtime = 0;
00277     strecpy(fios->name, "..", lastof(fios->name));
00278     strecpy(fios->title, ".. (Parent directory)", lastof(fios->title));
00279   }
00280 
00281   /* Show subdirectories */
00282   if (mode != SLD_NEW_GAME && (dir = ttd_opendir(_fios_path)) != NULL) {
00283     while ((dirent = readdir(dir)) != NULL) {
00284       strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name));
00285 
00286       /* found file must be directory, but not '.' or '..' */
00287       if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
00288           (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
00289           strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
00290         fios = _fios_items.Append();
00291         fios->type = FIOS_TYPE_DIR;
00292         fios->mtime = 0;
00293         strecpy(fios->name, d_name, lastof(fios->name));
00294         snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name);
00295         str_validate(fios->title, lastof(fios->title));
00296       }
00297     }
00298     closedir(dir);
00299   }
00300 
00301   /* Sort the subdirs always by name, ascending, remember user-sorting order */
00302   {
00303     byte order = _savegame_sort_order;
00304     _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
00305     qsort(_fios_items.Begin(), _fios_items.Length(), sizeof(FiosItem), compare_FiosItems);
00306     _savegame_sort_order = order;
00307   }
00308 
00309   /* This is where to start sorting for the filenames */
00310   sort_start = _fios_items.Length();
00311 
00312   /* Show files */
00313   FiosFileScanner scanner(mode, callback_proc);
00314   if (subdir == NO_DIRECTORY) {
00315     scanner.Scan(NULL, _fios_path, false);
00316   } else {
00317     scanner.Scan(NULL, subdir, true, true);
00318   }
00319 
00320   qsort(_fios_items.Get(sort_start), _fios_items.Length() - sort_start, sizeof(FiosItem), compare_FiosItems);
00321 
00322   /* Show drives */
00323   if (mode != SLD_NEW_GAME) FiosGetDrives();
00324 
00325   _fios_items.Compact();
00326 }
00327 
00335 static void GetFileTitle(const char *file, char *title, const char *last)
00336 {
00337   char buf[MAX_PATH];
00338   strecpy(buf, file, lastof(buf));
00339   strecat(buf, ".title", lastof(buf));
00340 
00341   FILE *f = FioFOpenFile(buf, "r");
00342   if (f == NULL) return;
00343 
00344   size_t read = fread(title, 1, last - title, f);
00345   assert(title + read <= last);
00346   title[read] = '\0';
00347   str_validate(title, last);
00348   FioFCloseFile(f);
00349 }
00350 
00362 FiosType FiosGetSavegameListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00363 {
00364   /* Show savegame files
00365    * .SAV OpenTTD saved game
00366    * .SS1 Transport Tycoon Deluxe preset game
00367    * .SV1 Transport Tycoon Deluxe (Patch) saved game
00368    * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
00369   if (strcasecmp(ext, ".sav") == 0) {
00370     GetFileTitle(file, title, last);
00371     return FIOS_TYPE_FILE;
00372   }
00373 
00374   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) {
00375     if (strcasecmp(ext, ".ss1") == 0 || strcasecmp(ext, ".sv1") == 0 ||
00376         strcasecmp(ext, ".sv2") == 0) {
00377       if (title != NULL) GetOldSaveGameName(file, title, last);
00378       return FIOS_TYPE_OLDFILE;
00379     }
00380   }
00381 
00382   return FIOS_TYPE_INVALID;
00383 }
00384 
00391 void FiosGetSavegameList(SaveLoadDialogMode mode)
00392 {
00393   static char *fios_save_path = NULL;
00394 
00395   if (fios_save_path == NULL) {
00396     fios_save_path = MallocT<char>(MAX_PATH);
00397     FioGetDirectory(fios_save_path, MAX_PATH, SAVE_DIR);
00398   }
00399 
00400   _fios_path = fios_save_path;
00401 
00402   FiosGetFileList(mode, &FiosGetSavegameListCallback, NO_DIRECTORY);
00403 }
00404 
00416 static FiosType FiosGetScenarioListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00417 {
00418   /* Show scenario files
00419    * .SCN OpenTTD style scenario file
00420    * .SV0 Transport Tycoon Deluxe (Patch) scenario
00421    * .SS0 Transport Tycoon Deluxe preset scenario */
00422   if (strcasecmp(ext, ".scn") == 0) {
00423     GetFileTitle(file, title, last);
00424     return FIOS_TYPE_SCENARIO;
00425   }
00426 
00427   if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO || mode == SLD_NEW_GAME) {
00428     if (strcasecmp(ext, ".sv0") == 0 || strcasecmp(ext, ".ss0") == 0 ) {
00429       GetOldSaveGameName(file, title, last);
00430       return FIOS_TYPE_OLD_SCENARIO;
00431     }
00432   }
00433 
00434   return FIOS_TYPE_INVALID;
00435 }
00436 
00443 void FiosGetScenarioList(SaveLoadDialogMode mode)
00444 {
00445   static char *fios_scn_path = NULL;
00446 
00447   /* Copy the default path on first run or on 'New Game' */
00448   if (fios_scn_path == NULL) {
00449     fios_scn_path = MallocT<char>(MAX_PATH);
00450     FioGetDirectory(fios_scn_path, MAX_PATH, SCENARIO_DIR);
00451   }
00452 
00453   _fios_path = fios_scn_path;
00454 
00455   char base_path[MAX_PATH];
00456   FioGetDirectory(base_path, sizeof(base_path), SCENARIO_DIR);
00457 
00458   FiosGetFileList(mode, &FiosGetScenarioListCallback, (mode == SLD_LOAD_SCENARIO && strcmp(base_path, _fios_path) == 0) ? SCENARIO_DIR : NO_DIRECTORY);
00459 }
00460 
00461 static FiosType FiosGetHeightmapListCallback(SaveLoadDialogMode mode, const char *file, const char *ext, char *title, const char *last)
00462 {
00463   /* Show heightmap files
00464    * .PNG PNG Based heightmap files
00465    * .BMP BMP Based heightmap files
00466    */
00467 
00468   FiosType type = FIOS_TYPE_INVALID;
00469 
00470 #ifdef WITH_PNG
00471   if (strcasecmp(ext, ".png") == 0) type = FIOS_TYPE_PNG;
00472 #endif /* WITH_PNG */
00473 
00474   if (strcasecmp(ext, ".bmp") == 0) type = FIOS_TYPE_BMP;
00475 
00476   if (type != FIOS_TYPE_INVALID) GetFileTitle(file, title, last);
00477 
00478   return type;
00479 }
00480 
00481 /* Get a list of Heightmaps */
00482 void FiosGetHeightmapList(SaveLoadDialogMode mode)
00483 {
00484   static char *fios_hmap_path = NULL;
00485 
00486   if (fios_hmap_path == NULL) {
00487     fios_hmap_path = MallocT<char>(MAX_PATH);
00488     FioGetDirectory(fios_hmap_path, MAX_PATH, HEIGHTMAP_DIR);
00489   }
00490 
00491   _fios_path = fios_hmap_path;
00492 
00493   char base_path[MAX_PATH];
00494   FioGetDirectory(base_path, sizeof(base_path), HEIGHTMAP_DIR);
00495 
00496   FiosGetFileList(mode, &FiosGetHeightmapListCallback, strcmp(base_path, _fios_path) == 0 ? HEIGHTMAP_DIR : NO_DIRECTORY);
00497 }
00498 
00499 #if defined(ENABLE_NETWORK)
00500 #include "core/smallvec_type.hpp"
00501 #include "network/network_content.h"
00502 #include "md5.h"
00503 
00505 struct ScenarioIdentifier {
00506   uint32 scenid;    
00507   uint8 md5sum[16]; 
00508 
00509   bool operator == (const ScenarioIdentifier &other) const
00510   {
00511     return this->scenid == other.scenid &&
00512         memcmp(this->md5sum, other.md5sum, sizeof(this->md5sum)) == 0;
00513   }
00514 
00515   bool operator != (const ScenarioIdentifier &other) const
00516   {
00517     return !(*this == other);
00518   }
00519 };
00520 
00524 class ScenarioScanner : protected FileScanner, public SmallVector<ScenarioIdentifier, 8> {
00525   bool scanned; 
00526 public:
00528   ScenarioScanner() : scanned(false) {}
00529 
00534   void Scan(bool rescan)
00535   {
00536     if (this->scanned && !rescan) return;
00537 
00538     this->FileScanner::Scan(".id", SCENARIO_DIR, true, true);
00539     this->scanned = true;
00540   }
00541 
00542   /* virtual */ bool AddFile(const char *filename, size_t basepath_length)
00543   {
00544     FILE *f = FioFOpenFile(filename, "r");
00545     if (f == NULL) return false;
00546 
00547     ScenarioIdentifier id;
00548     int fret = fscanf(f, "%i", &id.scenid);
00549     FioFCloseFile(f);
00550     if (fret != 1) return false;
00551 
00552     Md5 checksum;
00553     uint8 buffer[1024];
00554     size_t len, size;
00555 
00556     /* open the scenario file, but first get the name.
00557      * This is safe as we check on extension which
00558      * must always exist. */
00559     *(char *)strrchr(filename, '.') = '\0';
00560     f = FioFOpenFile(filename, "rb", SCENARIO_DIR, &size);
00561     if (f == NULL) return false;
00562 
00563     /* calculate md5sum */
00564     while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00565       size -= len;
00566       checksum.Append(buffer, len);
00567     }
00568     checksum.Finish(id.md5sum);
00569 
00570     FioFCloseFile(f);
00571 
00572     this->Include(id);
00573     return true;
00574   }
00575 };
00576 
00578 static ScenarioScanner _scanner;
00579 
00586 bool HasScenario(const ContentInfo *ci, bool md5sum)
00587 {
00588   _scanner.Scan(false);
00589 
00590   for (ScenarioIdentifier *id = _scanner.Begin(); id != _scanner.End(); id++) {
00591     if (md5sum ?
00592         (memcmp(id->md5sum, ci->md5sum, sizeof(id->md5sum)) == 0) :
00593         (id->scenid == ci->unique_id)) {
00594       return true;
00595     }
00596   }
00597 
00598   return false;
00599 }
00600 
00604 void ScanScenarios()
00605 {
00606   _scanner.Scan(true);
00607 }
00608 
00609 #endif /* ENABLE_NETWORK */

Generated on Mon Mar 9 23:33:47 2009 for openttd by  doxygen 1.5.6