16 #include "3rdparty/md5/md5.h" 29 #include "table/strings.h" 34 static char *_fios_path;
35 static const char *_fios_path_last;
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 char *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) {
110 for (
const FiosItem *item = this->Begin(); item != this->End(); item++) {
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;
120 if (
IsInsideMM(i, 0, this->Length()))
return this->Get(i);
124 char long_file[MAX_PATH];
126 for (
const FiosItem *item = this->Begin(); item != this->End(); item++) {
127 if (strcmp(long_file, item->name) == 0)
return item;
128 if (strcmp(long_file, item->title) == 0)
return item;
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 seprintf(_fios_path, _fios_path_last,
"%c:" PATHSEP, item->title[0]);
161 case FIOS_TYPE_INVALID:
164 case FIOS_TYPE_PARENT: {
166 char *s = strrchr(_fios_path, PATHSEPCHAR);
167 if (s !=
nullptr && s != _fios_path) {
170 s = strrchr(_fios_path, PATHSEPCHAR);
178 strecat(_fios_path, item->name, _fios_path_last);
179 strecat(_fios_path, PATHSEP, _fios_path_last);
182 case FIOS_TYPE_DIRECT:
183 seprintf(_fios_path, _fios_path_last,
"%s", item->name);
187 case FIOS_TYPE_OLDFILE:
188 case FIOS_TYPE_SCENARIO:
189 case FIOS_TYPE_OLD_SCENARIO:
206 static void FiosMakeFilename(
char *buf,
const char *path,
const char *name,
const char *ext,
const char *last)
208 if (path !=
nullptr) {
209 const char *buf_start = buf;
210 buf =
strecpy(buf, path, last);
212 if (buf > buf_start && buf[-1] == PATHSEPCHAR) buf--;
216 const char *period = strrchr(name,
'.');
217 if (period !=
nullptr && strcasecmp(period, ext) == 0) ext =
"";
219 seprintf(buf, last, PATHSEP
"%s%s", name, ext);
230 const char *extension = (_game_mode == GM_EDITOR) ?
".scn" :
".sav";
260 return unlink(filename) == 0;
263 typedef FiosType fios_getlist_callback_proc(
SaveLoadOperation fop,
const char *filename,
const char *ext,
char *title,
const char *last);
270 fios_getlist_callback_proc *callback_proc;
280 fop(fop), callback_proc(callback_proc), file_list(file_list)
283 bool AddFile(
const char *filename,
size_t basepath_length,
const char *tar_filename)
override;
294 const char *ext = strrchr(filename,
'.');
295 if (ext ==
nullptr)
return false;
298 fios_title[0] =
'\0';
300 FiosType type = this->callback_proc(this->fop, filename, ext, fios_title,
lastof(fios_title));
301 if (type == FIOS_TYPE_INVALID)
return false;
303 for (
const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) {
304 if (strcmp(fios->name, filename) == 0)
return false;
307 FiosItem *fios = file_list.Append();
310 HANDLE fh = CreateFile(
OTTD2FS(filename), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, 0,
nullptr);
312 if (fh != INVALID_HANDLE_VALUE) {
314 ULARGE_INTEGER ft_int64;
316 if (GetFileTime(fh,
nullptr,
nullptr, &ft) != 0) {
317 ft_int64.HighPart = ft.dwHighDateTime;
318 ft_int64.LowPart = ft.dwLowDateTime;
321 fios->mtime = ft_int64.QuadPart / 10000000ULL - 11644473600ULL;
329 if (stat(filename, &sb) == 0) {
330 fios->mtime = sb.st_mtime;
340 const char *t = fios_title;
342 t = strrchr(filename, PATHSEPCHAR);
343 t = (t ==
nullptr) ? filename : (t + 1);
362 struct dirent *dirent;
366 char d_name[
sizeof(fios->name)];
371 if (!FiosIsRoot(_fios_path)) {
372 fios = file_list.
Append();
373 fios->type = FIOS_TYPE_PARENT;
376 strecpy(fios->title,
".. (Parent directory)",
lastof(fios->title));
381 while ((dirent = readdir(dir)) !=
nullptr) {
385 if (FiosIsValidFile(_fios_path, dirent, &sb) && S_ISDIR(sb.st_mode) &&
386 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
387 strcmp(d_name,
".") != 0 && strcmp(d_name,
"..") != 0) {
388 fios = file_list.
Append();
389 fios->type = FIOS_TYPE_DIR;
392 seprintf(fios->title,
lastof(fios->title),
"%s" PATHSEP
" (Directory)", d_name);
401 SortingBits order = _savegame_sort_order;
402 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
403 std::sort(file_list.
files.begin(), file_list.
files.end());
404 _savegame_sort_order = order;
408 sort_start = file_list.
Length();
413 scanner.
Scan(
nullptr, _fios_path,
false);
415 scanner.
Scan(
nullptr, subdir,
true,
true);
418 std::sort(file_list.
files.begin() + sort_start, file_list.
files.end());
421 FiosGetDrives(file_list);
441 if (f ==
nullptr)
return;
443 size_t read = fread(title, 1, last - title, f);
444 assert(title + read <= last);
470 if (ext ==
nullptr)
return FIOS_TYPE_INVALID;
472 if (strcasecmp(ext,
".sav") == 0) {
474 return FIOS_TYPE_FILE;
478 if (strcasecmp(ext,
".ss1") == 0 || strcasecmp(ext,
".sv1") == 0 ||
479 strcasecmp(ext,
".sv2") == 0) {
480 if (title !=
nullptr) GetOldSaveGameName(file, title, last);
481 return FIOS_TYPE_OLDFILE;
485 return FIOS_TYPE_INVALID;
496 static char *fios_save_path =
nullptr;
497 static char *fios_save_path_last =
nullptr;
499 if (fios_save_path ==
nullptr) {
500 fios_save_path = MallocT<char>(MAX_PATH);
501 fios_save_path_last = fios_save_path + MAX_PATH - 1;
502 FioGetDirectory(fios_save_path, fios_save_path_last,
SAVE_DIR);
505 _fios_path = fios_save_path;
506 _fios_path_last = fios_save_path_last;
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 char *fios_scn_path =
nullptr;
552 static char *fios_scn_path_last =
nullptr;
555 if (fios_scn_path ==
nullptr) {
556 fios_scn_path = MallocT<char>(MAX_PATH);
557 fios_scn_path_last = fios_scn_path + MAX_PATH - 1;
558 FioGetDirectory(fios_scn_path, fios_scn_path_last,
SCENARIO_DIR);
561 _fios_path = fios_scn_path;
562 _fios_path_last = fios_scn_path_last;
564 char base_path[MAX_PATH];
571 static FiosType FiosGetHeightmapListCallback(
SaveLoadOperation fop,
const char *file,
const char *ext,
char *title,
const char *last)
581 if (strcasecmp(ext,
".png") == 0) type = FIOS_TYPE_PNG;
584 if (strcasecmp(ext,
".bmp") == 0) type = FIOS_TYPE_BMP;
586 if (type == FIOS_TYPE_INVALID)
return FIOS_TYPE_INVALID;
588 TarFileList::iterator it = _tar_filelist[
SCENARIO_DIR].find(file);
601 if (strncmp(buf, it->second.tar_filename, strlen(buf)) == 0) {
607 if (!match)
return FIOS_TYPE_INVALID;
622 static char *fios_hmap_path =
nullptr;
623 static char *fios_hmap_path_last =
nullptr;
625 if (fios_hmap_path ==
nullptr) {
626 fios_hmap_path = MallocT<char>(MAX_PATH);
627 fios_hmap_path_last = fios_hmap_path + MAX_PATH - 1;
628 FioGetDirectory(fios_hmap_path, fios_hmap_path_last,
HEIGHTMAP_DIR);
631 _fios_path = fios_hmap_path;
632 _fios_path_last = fios_hmap_path_last;
634 char base_path[MAX_PATH];
638 FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list);
647 static char *fios_screenshot_path =
nullptr;
649 if (fios_screenshot_path ==
nullptr) {
650 fios_screenshot_path = MallocT<char>(MAX_PATH);
651 FioGetDirectory(fios_screenshot_path, fios_screenshot_path + MAX_PATH - 1,
SCREENSHOT_DIR);
654 return fios_screenshot_path;
661 char filename[MAX_PATH];
665 return this->scenid == other.
scenid &&
666 memcmp(this->md5sum, other.
md5sum,
sizeof(this->md5sum)) == 0;
671 return !(*
this == other);
690 if (this->scanned && !rescan)
return;
693 this->scanned =
true;
696 bool AddFile(
const char *filename,
size_t basepath_length,
const char *tar_filename)
override 699 if (f ==
nullptr)
return false;
702 int fret = fscanf(f,
"%i", &
id.scenid);
704 if (fret != 1)
return false;
709 char basename[MAX_PATH];
716 *strrchr(basename,
'.') =
'\0';
718 if (f ==
nullptr)
return false;
721 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, f)) != 0 && size != 0) {
723 checksum.Append(buffer, len);
725 checksum.Finish(
id.md5sum);
745 _scanner.
Scan(
false);
748 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.