14 #include "3rdparty/md5/md5.h" 27 #include "table/strings.h" 32 static char *_fios_path;
33 static const char *_fios_path_last;
34 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
37 extern bool FiosIsRoot(
const char *path);
38 extern bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb);
39 extern bool FiosIsHiddenFile(
const struct dirent *ent);
40 extern void FiosGetDrives(
FileList &file_list);
41 extern bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot);
44 extern void GetOldSaveGameName(
const char *file,
char *title,
const char *last);
55 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && (*this).mtime != other.mtime) {
56 r = (*this).mtime - other.mtime;
58 r =
strnatcmp((*this).title, other.title);
60 if (r == 0)
return false;
61 return (_savegame_sort_order & SORT_DESCENDING) ? r > 0 : r < 0;
79 switch (abstract_filetype) {
108 for (
const FiosItem *item = this->Begin(); item != this->End(); item++) {
109 if (strcmp(file, item->name) == 0)
return item;
110 if (strcmp(file, item->title) == 0)
return item;
115 int i = strtol(file, &endptr, 10);
116 if (file == endptr || *endptr !=
'\0') i = -1;
118 if (
IsInsideMM(i, 0, this->Length()))
return this->Get(i);
122 char long_file[MAX_PATH];
124 for (
const FiosItem *item = this->Begin(); item != this->End(); item++) {
125 if (strcmp(long_file, item->name) == 0)
return item;
126 if (strcmp(long_file, item->title) == 0)
return item;
142 return FiosGetDiskFreeSpace(*path, total_free) ? STR_SAVELOAD_BYTES_FREE : STR_ERROR_UNABLE_TO_READ_DRIVE;
152 switch (item->type) {
153 case FIOS_TYPE_DRIVE:
154 #if defined(_WIN32) || defined(__OS2__) 155 seprintf(_fios_path, _fios_path_last,
"%c:" PATHSEP, item->title[0]);
159 case FIOS_TYPE_INVALID:
162 case FIOS_TYPE_PARENT: {
164 char *s = strrchr(_fios_path, PATHSEPCHAR);
165 if (s !=
nullptr && s != _fios_path) {
168 s = strrchr(_fios_path, PATHSEPCHAR);
176 strecat(_fios_path, item->name, _fios_path_last);
177 strecat(_fios_path, PATHSEP, _fios_path_last);
180 case FIOS_TYPE_DIRECT:
181 seprintf(_fios_path, _fios_path_last,
"%s", item->name);
185 case FIOS_TYPE_OLDFILE:
186 case FIOS_TYPE_SCENARIO:
187 case FIOS_TYPE_OLD_SCENARIO:
204 static void FiosMakeFilename(
char *buf,
const char *path,
const char *name,
const char *ext,
const char *last)
206 if (path !=
nullptr) {
207 const char *buf_start = buf;
208 buf =
strecpy(buf, path, last);
210 if (buf > buf_start && buf[-1] == PATHSEPCHAR) buf--;
214 const char *period = strrchr(name,
'.');
215 if (period !=
nullptr && strcasecmp(period, ext) == 0) ext =
"";
217 seprintf(buf, last, PATHSEP
"%s%s", name, ext);
228 const char *extension = (_game_mode == GM_EDITOR) ?
".scn" :
".sav";
258 return unlink(filename) == 0;
261 typedef FiosType fios_getlist_callback_proc(
SaveLoadOperation fop,
const char *filename,
const char *ext,
char *title,
const char *last);
268 fios_getlist_callback_proc *callback_proc;
278 fop(fop), callback_proc(callback_proc), file_list(file_list)
281 bool AddFile(
const char *filename,
size_t basepath_length,
const char *tar_filename)
override;
292 const char *ext = strrchr(filename,
'.');
293 if (ext ==
nullptr)
return false;
296 fios_title[0] =
'\0';
298 FiosType type = this->callback_proc(this->fop, filename, ext, fios_title,
lastof(fios_title));
299 if (type == FIOS_TYPE_INVALID)
return false;
301 for (
const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) {
302 if (strcmp(fios->name, filename) == 0)
return false;
305 FiosItem *fios = file_list.Append();
308 HANDLE fh = CreateFile(
OTTD2FS(filename), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, 0,
nullptr);
310 if (fh != INVALID_HANDLE_VALUE) {
312 ULARGE_INTEGER ft_int64;
314 if (GetFileTime(fh,
nullptr,
nullptr, &ft) != 0) {
315 ft_int64.HighPart = ft.dwHighDateTime;
316 ft_int64.LowPart = ft.dwLowDateTime;
319 fios->mtime = ft_int64.QuadPart / 10000000ULL - 11644473600ULL;
327 if (stat(filename, &sb) == 0) {
328 fios->mtime = sb.st_mtime;
338 const char *t = fios_title;
340 t = strrchr(filename, PATHSEPCHAR);
341 t = (t ==
nullptr) ? filename : (t + 1);
360 struct dirent *dirent;
364 char d_name[
sizeof(fios->name)];
369 if (!FiosIsRoot(_fios_path)) {
370 fios = file_list.
Append();
371 fios->type = FIOS_TYPE_PARENT;
374 strecpy(fios->title,
".. (Parent directory)",
lastof(fios->title));
379 while ((dirent = readdir(dir)) !=
nullptr) {
383 if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
384 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
385 strcmp(d_name,
".") != 0 && strcmp(d_name,
"..") != 0) {
386 fios = file_list.
Append();
387 fios->type = FIOS_TYPE_DIR;
390 seprintf(fios->title,
lastof(fios->title),
"%s" PATHSEP
" (Directory)", d_name);
399 SortingBits order = _savegame_sort_order;
400 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
401 std::sort(file_list.
files.begin(), file_list.
files.end());
402 _savegame_sort_order = order;
406 sort_start = file_list.
Length();
411 scanner.
Scan(
nullptr, _fios_path,
false);
413 scanner.
Scan(
nullptr, subdir,
true,
true);
416 std::sort(file_list.
files.begin() + sort_start, file_list.
files.end());
419 FiosGetDrives(file_list);
439 if (f ==
nullptr)
return;
441 size_t read = fread(title, 1, last - title, f);
442 assert(title + read <= last);
468 if (ext ==
nullptr)
return FIOS_TYPE_INVALID;
470 if (strcasecmp(ext,
".sav") == 0) {
472 return FIOS_TYPE_FILE;
476 if (strcasecmp(ext,
".ss1") == 0 || strcasecmp(ext,
".sv1") == 0 ||
477 strcasecmp(ext,
".sv2") == 0) {
478 if (title !=
nullptr) GetOldSaveGameName(file, title, last);
479 return FIOS_TYPE_OLDFILE;
483 return FIOS_TYPE_INVALID;
494 static char *fios_save_path =
nullptr;
495 static char *fios_save_path_last =
nullptr;
497 if (fios_save_path ==
nullptr) {
498 fios_save_path = MallocT<char>(MAX_PATH);
499 fios_save_path_last = fios_save_path + MAX_PATH - 1;
500 FioGetDirectory(fios_save_path, fios_save_path_last,
SAVE_DIR);
503 _fios_path = fios_save_path;
504 _fios_path_last = fios_save_path_last;
526 if (strcasecmp(ext,
".scn") == 0) {
528 return FIOS_TYPE_SCENARIO;
532 if (strcasecmp(ext,
".sv0") == 0 || strcasecmp(ext,
".ss0") == 0 ) {
533 GetOldSaveGameName(file, title, last);
534 return FIOS_TYPE_OLD_SCENARIO;
538 return FIOS_TYPE_INVALID;
549 static char *fios_scn_path =
nullptr;
550 static char *fios_scn_path_last =
nullptr;
553 if (fios_scn_path ==
nullptr) {
554 fios_scn_path = MallocT<char>(MAX_PATH);
555 fios_scn_path_last = fios_scn_path + MAX_PATH - 1;
556 FioGetDirectory(fios_scn_path, fios_scn_path_last,
SCENARIO_DIR);
559 _fios_path = fios_scn_path;
560 _fios_path_last = fios_scn_path_last;
562 char base_path[MAX_PATH];
569 static FiosType FiosGetHeightmapListCallback(
SaveLoadOperation fop,
const char *file,
const char *ext,
char *title,
const char *last)
579 if (strcasecmp(ext,
".png") == 0) type = FIOS_TYPE_PNG;
582 if (strcasecmp(ext,
".bmp") == 0) type = FIOS_TYPE_BMP;
584 if (type == FIOS_TYPE_INVALID)
return FIOS_TYPE_INVALID;
586 TarFileList::iterator it = _tar_filelist[
SCENARIO_DIR].find(file);
599 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
605 if (!match)
return FIOS_TYPE_INVALID;
620 static char *fios_hmap_path =
nullptr;
621 static char *fios_hmap_path_last =
nullptr;
623 if (fios_hmap_path ==
nullptr) {
624 fios_hmap_path = MallocT<char>(MAX_PATH);
625 fios_hmap_path_last = fios_hmap_path + MAX_PATH - 1;
626 FioGetDirectory(fios_hmap_path, fios_hmap_path_last,
HEIGHTMAP_DIR);
629 _fios_path = fios_hmap_path;
630 _fios_path_last = fios_hmap_path_last;
632 char base_path[MAX_PATH];
636 FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list);
645 static char *fios_screenshot_path =
nullptr;
647 if (fios_screenshot_path ==
nullptr) {
648 fios_screenshot_path = MallocT<char>(MAX_PATH);
649 FioGetDirectory(fios_screenshot_path, fios_screenshot_path + MAX_PATH - 1,
SCREENSHOT_DIR);
652 return fios_screenshot_path;
659 char filename[MAX_PATH];
663 return this->scenid == other.
scenid &&
664 memcmp(this->md5sum, other.
md5sum,
sizeof(this->md5sum)) == 0;
669 return !(*
this == other);
688 if (this->scanned && !rescan)
return;
691 this->scanned =
true;
694 bool AddFile(
const char *filename,
size_t basepath_length,
const char *tar_filename)
override 697 if (f ==
nullptr)
return false;
700 int fret = fscanf(f,
"%i", &
id.scenid);
702 if (fret != 1)
return false;
707 char basename[MAX_PATH];
714 *strrchr(basename,
'.') =
'\0';
716 if (f ==
nullptr)
return false;
719 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, f)) != 0 && size != 0) {
721 checksum.Append(buffer, len);
723 checksum.Finish(
id.md5sum);
743 _scanner.
Scan(
false);
746 if (md5sum ? (memcmp(
id.md5sum, ci->
md5sum,
sizeof(
id.md5sum)) == 0)
bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename) override
Try to add a fios item set with the given filename.
FiosType
Elements of a file system that are recognized.
AbstractFileType
The different abstract types of files that the system knows about.
Basic data to distinguish a scenario.
uint32 unique_id
Unique ID; either GRF ID or shortname.
Scanner to find the unique IDs of scenarios.
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last)
Callback for FiosGetFileList.
bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename) override
Add a file with the given filename.
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
const char * FS2OTTD(const TCHAR *name)
Convert to OpenTTD's encoding from that of the local environment.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Structs, typedefs and macros used for TAR file handling.
void Clear()
Remove all items from the list.
uint8 md5sum[16]
MD5 checksum of file.
std::vector< FiosItem > files
The list of files.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
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.
size_t Length() const
Get the number of files in the list.
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
Subdirectory of scenario for heightmaps.
Subdirectory for all screenshots.
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
Get a list of savegames.
Functions for Standard In/Out file operations.
#define lastof(x)
Get the last element of an fixed size array.
Functions to make screenshots.
Searchpath
Types of searchpaths OpenTTD might use.
uint32 scenid
ID for the scenario (generated by content).
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list)
Get a list of scenarios.
Deals with finding savegames.
StringID FiosGetDescText(const char **path, uint64 *total_free)
Get descriptive texts.
Helper for scanning for files with a given name.
const FiosItem * FindItem(const char *file)
Find file information of a file by its name from the file list.
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
bool HasScenario(const ContentInfo *ci, bool md5sum)
Check whether we've got a given scenario based on its unique ID.
FiosFileScanner(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, FileList &file_list)
Create the scanner.
Functions related to low-level strings.
Base directory for all scenarios.
const char * FindScenario(const ContentInfo *ci, bool md5sum)
Find a given scenario based on its unique ID.
static ScenarioScanner _scanner
Scanner for scenarios.
bool scanned
Whether we've already scanned.
void FiosMakeSavegameName(char *buf, const char *name, const char *last)
Make a save game or scenario filename from a name.
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==().
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...
static void FiosMakeFilename(char *buf, const char *path, const char *name, const char *ext, const char *last)
Construct a filename from its components in destination buffer buf.
FILE * FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
A path without any base directory.
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
void Scan(bool rescan)
Scan, but only if it's needed.
void ScanScenarios()
Force a (re)scan of the scenarios.
Base directory for all savegames.
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.
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop)
Construct a file list with the given kind of files, for the stated purpose.
FileList & file_list
Destination of the found files.
Part of the network protocol handling content distribution.
static void GetFileTitle(const char *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...
uint32 StringID
Numeric value that represents a string, independent of the selected language.
byte md5sum[16]
The MD5 checksum.
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to that of the local environment.
SaveLoadOperation
Operation performed on the file.
void Compact()
Compact the list down to the smallest block size boundary.
bool FiosDelete(const char *name)
Delete a file.
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list)
Get a list of heightmaps.
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.
void FiosMakeHeightmapName(char *buf, const char *name, const char *last)
Construct a filename for a height map.
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Declarations for savegames operations.
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...
List of file information.
const char * FiosBrowseTo(const FiosItem *item)
Browse to a new path based on the passed item, starting at #_fios_path.
static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const char *file, const char *ext, char *title, const char *last)
Callback for FiosGetFileList.
Scanner to scan for a particular type of FIOS file.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
const char * GetCurrentScreenshotExtension()
Get filename extension of current screenshot file format.
static DIR * ttd_opendir(const char *path)
A wrapper around opendir() which will convert the string from OPENTTD encoding to that of the filesys...
SaveLoadOperation fop
The kind of file we are looking for.
FiosItem * Append()
Construct a new entry in the file list.
bool operator<(const FiosItem &other) const
Compare two FiosItem's.
ScenarioScanner()
Initialise.
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
Container for all important information about a piece of content.