fios.cpp

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

Generated on Tue Jul 21 18:48:23 2009 for OpenTTD by  doxygen 1.5.6