Go to the documentation of this file.
14 #include "3rdparty/md5/md5.h"
30 #include "table/strings.h"
35 static std::string *_fios_path =
nullptr;
36 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
39 extern bool FiosIsRoot(
const char *path);
40 extern bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb);
41 extern bool FiosIsHiddenFile(
const struct dirent *ent);
42 extern void FiosGetDrives(
FileList &file_list);
43 extern bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot);
46 extern void GetOldSaveGameName(
const std::string &file,
char *title,
const char *last);
57 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && (*this).mtime != other.mtime) {
58 r = (*this).mtime - other.mtime;
60 r =
strnatcmp((*this).title, other.title);
62 if (r == 0)
return false;
63 return (_savegame_sort_order & SORT_DESCENDING) ? r > 0 : r < 0;
81 switch (abstract_filetype) {
111 if (strcmp(file, item->name) == 0)
return item;
112 if (strcmp(file, item->title) == 0)
return item;
117 int i = strtol(file, &endptr, 10);
118 if (file == endptr || *endptr !=
'\0') i = -1;
124 char long_file[MAX_PATH];
127 if (strcmp(long_file, item->name) == 0)
return item;
128 if (strcmp(long_file, item->title) == 0)
return item;
143 *path = _fios_path->c_str();
144 return FiosGetDiskFreeSpace(*path, total_free) ? STR_SAVELOAD_BYTES_FREE : STR_ERROR_UNABLE_TO_READ_DRIVE;
154 switch (item->type) {
155 case FIOS_TYPE_DRIVE:
156 #if defined(_WIN32) || defined(__OS2__)
157 assert(_fios_path !=
nullptr);
158 *_fios_path = std::string{ item->title[0] } +
":" PATHSEP;
162 case FIOS_TYPE_INVALID:
165 case FIOS_TYPE_PARENT: {
166 assert(_fios_path !=
nullptr);
167 auto s = _fios_path->find_last_of(PATHSEPCHAR);
168 if (s != std::string::npos && s != 0) {
169 _fios_path->erase(s);
172 s = _fios_path->find_last_of(PATHSEPCHAR);
173 if (s != std::string::npos) {
174 _fios_path->erase(s + 1);
180 assert(_fios_path !=
nullptr);
181 *_fios_path += item->name;
182 *_fios_path += PATHSEP;
185 case FIOS_TYPE_DIRECT:
186 assert(_fios_path !=
nullptr);
187 *_fios_path = item->name;
191 case FIOS_TYPE_OLDFILE:
192 case FIOS_TYPE_SCENARIO:
193 case FIOS_TYPE_OLD_SCENARIO:
209 static std::string
FiosMakeFilename(
const std::string *path,
const char *name,
const char *ext)
213 if (path !=
nullptr) {
216 if (!buf.empty() && buf.back() == PATHSEPCHAR) buf.pop_back();
220 const char *period = strrchr(name,
'.');
221 if (period !=
nullptr && strcasecmp(period, ext) == 0) ext =
"";
223 return buf + PATHSEP + name + ext;
235 const char *extension = (_game_mode == GM_EDITOR) ?
".scn" :
".sav";
247 std::string ext(
".");
261 return unlink(filename.c_str()) == 0;
264 typedef FiosType fios_getlist_callback_proc(
SaveLoadOperation fop,
const std::string &filename,
const char *ext,
char *title,
const char *last);
284 bool AddFile(
const std::string &filename,
size_t basepath_length,
const std::string &tar_filename)
override;
295 auto sep = filename.rfind(
'.');
296 if (sep == std::string::npos)
return false;
297 std::string ext = filename.substr(sep);
300 fios_title[0] =
'\0';
303 if (type == FIOS_TYPE_INVALID)
return false;
306 if (filename == fios->name)
return false;
312 HANDLE fh = CreateFile(
OTTD2FS(filename.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, 0,
nullptr);
314 if (fh != INVALID_HANDLE_VALUE) {
316 ULARGE_INTEGER ft_int64;
318 if (GetFileTime(fh,
nullptr,
nullptr, &ft) != 0) {
319 ft_int64.HighPart = ft.dwHighDateTime;
320 ft_int64.LowPart = ft.dwLowDateTime;
323 fios->mtime = ft_int64.QuadPart / 10000000ULL - 11644473600ULL;
331 if (stat(filename.c_str(), &sb) == 0) {
332 fios->mtime = sb.st_mtime;
342 const char *t = fios_title;
344 auto ps = filename.rfind(PATHSEPCHAR);
345 t = filename.c_str() + (ps == std::string::npos ? 0 : ps + 1);
364 struct dirent *dirent;
368 char d_name[
sizeof(fios->name)];
372 assert(_fios_path !=
nullptr);
375 if (!FiosIsRoot(_fios_path->c_str())) {
377 fios->type = FIOS_TYPE_PARENT;
381 GetString(fios->title, STR_SAVELOAD_PARENT_DIRECTORY,
lastof(fios->title));
385 if ((dir =
ttd_opendir(_fios_path->c_str())) !=
nullptr) {
386 while ((dirent = readdir(dir)) !=
nullptr) {
390 if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) &&
391 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
392 strcmp(d_name,
".") != 0 && strcmp(d_name,
"..") != 0) {
394 fios->type = FIOS_TYPE_DIR;
397 std::string dirname = std::string(d_name) + PATHSEP;
399 GetString(fios->title, STR_SAVELOAD_DIRECTORY,
lastof(fios->title));
408 SortingBits order = _savegame_sort_order;
409 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
411 _savegame_sort_order = order;
420 scanner.
Scan(
nullptr, _fios_path->c_str(),
false);
443 std::string buf = file;
447 if (f ==
nullptr)
return;
449 size_t read = fread(title, 1, last - title, f);
450 assert(title + read <= last);
476 if (ext ==
nullptr)
return FIOS_TYPE_INVALID;
478 if (strcasecmp(ext,
".sav") == 0) {
480 return FIOS_TYPE_FILE;
484 if (strcasecmp(ext,
".ss1") == 0 || strcasecmp(ext,
".sv1") == 0 ||
485 strcasecmp(ext,
".sv2") == 0) {
486 if (title !=
nullptr) GetOldSaveGameName(file, title, last);
487 return FIOS_TYPE_OLDFILE;
491 return FIOS_TYPE_INVALID;
502 static std::optional<std::string> fios_save_path;
504 if (!fios_save_path) fios_save_path = FioFindDirectory(
SAVE_DIR);
506 _fios_path = &(*fios_save_path);
528 if (strcasecmp(ext,
".scn") == 0) {
530 return FIOS_TYPE_SCENARIO;
534 if (strcasecmp(ext,
".sv0") == 0 || strcasecmp(ext,
".ss0") == 0 ) {
535 GetOldSaveGameName(file, title, last);
536 return FIOS_TYPE_OLD_SCENARIO;
540 return FIOS_TYPE_INVALID;
551 static std::optional<std::string> fios_scn_path;
554 if (!fios_scn_path) fios_scn_path = FioFindDirectory(
SCENARIO_DIR);
556 _fios_path = &(*fios_scn_path);
563 static FiosType FiosGetHeightmapListCallback(
SaveLoadOperation fop,
const std::string &file,
const char *ext,
char *title,
const char *last)
573 if (strcasecmp(ext,
".png") == 0) type = FIOS_TYPE_PNG;
576 if (strcasecmp(ext,
".bmp") == 0) type = FIOS_TYPE_BMP;
578 if (type == FIOS_TYPE_INVALID)
return FIOS_TYPE_INVALID;
580 TarFileList::iterator it = _tar_filelist[
SCENARIO_DIR].find(file);
592 if (buf.compare(0, buf.size(), it->second.tar_filename, 0, buf.size()) == 0) {
598 if (!match)
return FIOS_TYPE_INVALID;
613 static std::optional<std::string> fios_hmap_path;
615 if (!fios_hmap_path) fios_hmap_path = FioFindDirectory(
HEIGHTMAP_DIR);
617 _fios_path = &(*fios_hmap_path);
630 static std::optional<std::string> fios_screenshot_path;
632 if (!fios_screenshot_path) fios_screenshot_path = FioFindDirectory(
SCREENSHOT_DIR);
634 return fios_screenshot_path->c_str();
641 char filename[MAX_PATH];
645 return this->scenid == other.
scenid &&
646 memcmp(this->md5sum, other.
md5sum,
sizeof(this->md5sum)) == 0;
651 return !(*
this == other);
670 if (this->scanned && !rescan)
return;
673 this->scanned =
true;
676 bool AddFile(
const std::string &filename,
size_t basepath_length,
const std::string &tar_filename)
override
679 if (f ==
nullptr)
return false;
682 int fret = fscanf(f,
"%u", &
id.scenid);
684 if (fret != 1)
return false;
695 if (f ==
nullptr)
return false;
698 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, f)) != 0 && size != 0) {
700 checksum.Append(buffer, len);
702 checksum.Finish(
id.md5sum);
725 if (md5sum ? (memcmp(
id.md5sum, ci->
md5sum,
sizeof(
id.md5sum)) == 0)
@ FT_SCENARIO
old or new scenario
static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
Callback for FiosGetFileList.
StringID FiosGetDescText(const char **path, uint64 *total_free)
Get descriptive texts.
@ SAVE_DIR
Base directory for all savegames.
const FiosItem * End() const
Get a pointer behind the last file information.
const char * FS2OTTD(const wchar_t *name)
Convert to OpenTTD's encoding from wide characters.
void str_validate(char *str, const char *last, StringValidationSettings settings)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
void Compact()
Compact the list down to the smallest block size boundary.
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list)
Get a list of scenarios.
void ScanScenarios()
Force a (re)scan of the scenarios.
SaveLoadOperation
Operation performed on the file.
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
Searchpath
Types of searchpaths OpenTTD might use.
uint Scan(const char *extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
bool include(std::vector< T > &vec, const T &item)
Helper function to append an item to a vector if it is not already contained Consider using std::set,...
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
List of file information.
SaveLoadOperation fop
The kind of file we are looking for.
bool scanned
Whether we've already scanned.
void Clear()
Remove all items from the list.
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list)
Get a list of heightmaps.
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
AbstractFileType
The different abstract types of files that the system knows about.
static void GetFileTitle(const std::string &file, char *title, const char *last, Subdirectory subdir)
Get the title of a file, which (if exists) is stored in a file named the same as the data file but wi...
FileList & file_list
Destination of the found files.
@ SLO_LOAD
File is being loaded.
@ SLO_SAVE
File is being saved.
std::string FiosMakeHeightmapName(const char *name)
Construct a filename for a height map.
bool FiosDelete(const char *name)
Delete a file.
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override
Try to add a fios item set with the given filename.
const FiosItem * Get(size_t index) const
Get a pointer to the indicated file information.
void Scan(bool rescan)
Scan, but only if it's needed.
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
uint32 scenid
ID for the scenario (generated by content).
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop)
Construct a file list with the given kind of files, for the stated purpose.
Container for all important information about a piece of content.
Deals with finding savegames.
byte md5sum[16]
The MD5 checksum.
std::vector< FiosItem > files
The list of files.
bool operator==(const MultiMapIterator< Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcompare > &iter1, const MultiMapIterator< Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcompare > &iter2)
Compare two MultiMap iterators.
const char * FiosBrowseTo(const FiosItem *item)
Browse to a new path based on the passed item, starting at #_fios_path.
const FiosItem * FindItem(const char *file)
Find file information of a file by its name from the file list.
static std::string FiosMakeFilename(const std::string *path, const char *name, const char *ext)
Construct a filename from its components in destination buffer buf.
@ SCENARIO_DIR
Base directory for all scenarios.
Subdirectory subdir
The current sub directory we are searching through.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
@ FT_SAVEGAME
old or new savegame
fios_getlist_callback_proc * callback_proc
Callback to check whether the file may be added.
FiosItem * Append()
Construct a new entry in the file list.
ScenarioScanner()
Initialise.
uint32 StringID
Numeric value that represents a string, independent of the selected language.
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
Callback for FiosGetFileList.
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
Get a list of savegames.
@ NO_DIRECTORY
A path without any base directory.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
@ FT_HEIGHTMAP
heightmap file
Subdirectory
The different kinds of subdirectories OpenTTD uses.
FiosType
Elements of a file system that are recognized.
uint32 unique_id
Unique ID; either GRF ID or shortname.
Basic data to distinguish a scenario.
const char * GetCurrentScreenshotExtension()
Get filename extension of current screenshot file format.
Scanner to scan for a particular type of FIOS file.
Helper for scanning for files with a given name.
static ScenarioScanner _scanner
Scanner for scenarios.
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override
Add a file with the given filename.
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
uint8 md5sum[16]
MD5 checksum of file.
const FiosItem * Begin() const
Get a pointer to the first file information.
bool operator!=(const MultiMapIterator< Tmap_iter1, Tlist_iter1, Tkey, Tvalue1, Tcompare > &iter1, const MultiMapIterator< Tmap_iter2, Tlist_iter2, Tkey, Tvalue2, Tcompare > &iter2)
Inverse of operator==().
std::string FiosMakeSavegameName(const char *name)
Make a save game or scenario filename from a name.
FiosFileScanner(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, FileList &file_list)
Create the scanner.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
static DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
Scanner to find the unique IDs of scenarios.
const char * FindScenario(const ContentInfo *ci, bool md5sum)
Find a given scenario based on its unique ID.
#define lastof(x)
Get the last element of an fixed size array.
static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, Subdirectory subdir, FileList &file_list)
Fill the list of the files in a directory, according to some arbitrary rule.
const wchar_t * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to wide characters.
void SetDParamStr(uint n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
void FioFCloseFile(FILE *f)
Close a file in a safe way.
size_t Length() const
Get the number of files in the list.
bool HasScenario(const ContentInfo *ci, bool md5sum)
Check whether we've got a given scenario based on its unique ID.
bool operator<(const FiosItem &other) const
Compare two FiosItem's.