14#include "3rdparty/md5/md5.h"
26#include "table/strings.h"
31static std::string *_fios_path =
nullptr;
32SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
35extern bool FiosIsRoot(
const std::string &path);
36extern bool FiosIsHiddenFile(
const std::filesystem::path &path);
37extern void FiosGetDrives(
FileList &file_list);
40extern std::string GetOldSaveGameName(
const std::string &file);
51 if ((_savegame_sort_order & SORT_BY_NAME) == 0 && (*this).mtime != other.mtime) {
52 r = ClampTo<int32_t>(this->mtime - other.mtime);
56 if (r == 0)
return false;
57 return (_savegame_sort_order & SORT_DESCENDING) ? r > 0 : r < 0;
71 switch (abstract_filetype) {
104 for (
const auto &it : *
this) {
106 if (file == item->name)
return item;
107 if (file == item->title)
return item;
112 int i = std::strtol(file.data(), &endptr, 10);
113 if (file.data() == endptr || *endptr !=
'\0') i = -1;
115 if (
IsInsideMM(i, 0, this->size()))
return &this->at(i);
119 std::string long_file(file);
121 for (
const auto &it : *
this) {
123 if (long_file == item->name)
return item;
124 if (long_file == item->title)
return item;
145 switch (item->type) {
146 case FIOS_TYPE_DRIVE:
148 assert(_fios_path !=
nullptr);
149 *_fios_path = std::string{ item->title, 0, 1 } +
":" PATHSEP;
153 case FIOS_TYPE_INVALID:
156 case FIOS_TYPE_PARENT: {
157 assert(_fios_path !=
nullptr);
158 auto s = _fios_path->find_last_of(PATHSEPCHAR);
159 if (s != std::string::npos && s != 0) {
160 _fios_path->erase(s);
163 s = _fios_path->find_last_of(PATHSEPCHAR);
164 if (s != std::string::npos) {
165 _fios_path->erase(s + 1);
171 assert(_fios_path !=
nullptr);
172 *_fios_path += item->name;
173 *_fios_path += PATHSEP;
176 case FIOS_TYPE_DIRECT:
177 assert(_fios_path !=
nullptr);
178 *_fios_path = item->name;
182 case FIOS_TYPE_OLDFILE:
183 case FIOS_TYPE_SCENARIO:
184 case FIOS_TYPE_OLD_SCENARIO:
201static std::string
FiosMakeFilename(
const std::string *path,
const char *name,
const char *ext)
205 if (path !=
nullptr) {
208 if (!buf.empty() && buf.back() == PATHSEPCHAR) buf.pop_back();
212 const char *period = strrchr(name,
'.');
215 return buf + PATHSEP + name + ext;
225 const char *extension = (_game_mode == GM_EDITOR) ?
".scn" :
".sav";
237 std::string ext(
".");
253typedef std::tuple<FiosType, std::string> FiosGetTypeAndNameProc(
SaveLoadOperation fop,
const std::string &filename,
const std::string_view ext);
273 bool AddFile(
const std::string &filename,
size_t basepath_length,
const std::string &tar_filename)
override;
283 auto sep = filename.rfind(
'.');
284 if (sep == std::string::npos)
return false;
285 std::string ext = filename.substr(sep);
288 if (type == FIOS_TYPE_INVALID)
return false;
291 if (filename == fios.name)
return false;
296 std::error_code error_code;
297 auto write_time = std::filesystem::last_write_time(
OTTD2FS(filename), error_code);
301 fios->mtime = std::chrono::duration_cast<std::chrono::milliseconds>(write_time.time_since_epoch()).count();
305 fios->name = filename;
309 auto ps = filename.rfind(PATHSEPCHAR);
310 fios->title =
StrMakeValid(filename.substr((ps == std::string::npos ? 0 : ps + 1)));
329 size_t sort_start = 0;
333 assert(_fios_path !=
nullptr);
337 if (!FiosIsRoot(*_fios_path)) {
338 FiosItem &fios = file_list.emplace_back();
339 fios.type = FIOS_TYPE_PARENT;
342 fios.title =
GetString(STR_SAVELOAD_PARENT_DIRECTORY,
"..");
343 sort_start = file_list.size();
347 std::error_code error_code;
348 for (
const auto &dir_entry : std::filesystem::directory_iterator(
OTTD2FS(*_fios_path), error_code)) {
349 if (!dir_entry.is_directory())
continue;
350 if (FiosIsHiddenFile(dir_entry) && dir_entry.path().filename() != PERSONAL_DIR)
continue;
352 FiosItem &fios = file_list.emplace_back();
353 fios.type = FIOS_TYPE_DIR;
355 fios.name =
FS2OTTD(dir_entry.path().filename());
356 fios.title =
GetString(STR_SAVELOAD_DIRECTORY, fios.name + PATHSEP);
360 SortingBits order = _savegame_sort_order;
361 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
362 std::sort(file_list.begin() + sort_start, file_list.end());
363 _savegame_sort_order = order;
367 sort_start = file_list.size();
372 scanner.
Scan({}, *_fios_path,
false);
374 scanner.
Scan({}, subdir,
true,
true);
377 std::sort(file_list.begin() + sort_start, file_list.end());
380 FiosGetDrives(file_list);
393 if (!f.has_value())
return {};
396 size_t read = fread(title, 1,
lengthof(title), *f);
426 return { FIOS_TYPE_OLDFILE, GetOldSaveGameName(file) };
430 return { FIOS_TYPE_INVALID, {} };
442 static std::optional<std::string> fios_save_path;
444 if (!fios_save_path) fios_save_path = FioFindDirectory(
SAVE_DIR);
446 _fios_path = &(*fios_save_path);
473 return { FIOS_TYPE_OLD_SCENARIO, GetOldSaveGameName(file) };
477 return { FIOS_TYPE_INVALID, {} };
489 static std::optional<std::string> fios_scn_path;
492 if (!fios_scn_path) fios_scn_path = FioFindDirectory(
SCENARIO_DIR);
494 _fios_path = &(*fios_scn_path);
501std::tuple<FiosType, std::string> FiosGetHeightmapListCallback(
SaveLoadOperation,
const std::string &file,
const std::string_view ext)
516 if (type == FIOS_TYPE_INVALID)
return { FIOS_TYPE_INVALID, {} };
518 TarFileList::iterator it = _tar_filelist[
SCENARIO_DIR].find(file);
529 if (buf.compare(0, buf.size(), it->second.tar_filename, 0, buf.size()) == 0) {
535 if (!match)
return { FIOS_TYPE_INVALID, {} };
549 static std::optional<std::string> fios_hmap_path;
551 if (!fios_hmap_path) fios_hmap_path = FioFindDirectory(
HEIGHTMAP_DIR);
553 _fios_path = &(*fios_hmap_path);
557 FiosGetFileList(fop, show_dirs, &FiosGetHeightmapListCallback, subdir, file_list);
574 return { FIOS_TYPE_INVALID, {} };
585 static std::optional<std::string> fios_town_data_path;
587 if (!fios_town_data_path) fios_town_data_path = FioFindDirectory(
HEIGHTMAP_DIR);
589 _fios_path = &(*fios_town_data_path);
602 static std::optional<std::string> fios_screenshot_path;
604 if (!fios_screenshot_path) fios_screenshot_path = FioFindDirectory(
SCREENSHOT_DIR);
606 return fios_screenshot_path->c_str();
617 return this->scenid == other.
scenid && this->md5sum == other.
md5sum;
636 if (this->scanned && !rescan)
return;
639 this->scanned =
true;
642 bool AddFile(
const std::string &filename,
size_t,
const std::string &)
override
645 if (!f.has_value())
return false;
648 int fret = fscanf(*f,
"%u", &
id.scenid);
649 if (fret != 1)
return false;
650 id.filename = filename;
653 uint8_t buffer[1024];
660 if (!f.has_value())
return false;
663 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, *f)) != 0 && size != 0) {
665 checksum.Append(buffer, len);
667 checksum.Finish(
id.md5sum);
688 if (md5sum ? (
id.md5sum == ci->
md5sum)
690 return id.filename.c_str();
722 static std::optional<std::string> _autosave_path;
723 if (!_autosave_path) _autosave_path = FioFindDirectory(
AUTOSAVE_DIR);
725 static std::string _prefix;
728 static FiosGetTypeAndNameProc *proc = [](
SaveLoadOperation,
const std::string &file,
const std::string_view ext) {
729 if (
StrEqualsIgnoreCase(ext,
".sav") && file.starts_with(_prefix))
return std::tuple(FIOS_TYPE_FILE, std::string{});
730 return std::tuple(FIOS_TYPE_INVALID, std::string{});
734 _prefix = *_autosave_path + this->prefix;
739 scanner.
Scan(
".sav", *_autosave_path,
false);
742 if (list.begin() != list.end()) {
743 SortingBits order = _savegame_sort_order;
744 _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
745 std::sort(list.begin(), list.end());
746 _savegame_sort_order = order;
748 std::string_view name = list.begin()->title;
749 std::from_chars(name.data() + this->prefix.size(), name.data() + name.size(), this->number);
760 return fmt::format(
"{}{}.sav", this->prefix, this->number);
769 return fmt::format(
"-{}.sav", this->prefix);
List of file information.
const FiosItem * FindItem(const std::string_view file)
Find file information of a file by its name from the file list.
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs)
Construct a file list with the given kind of files, for the stated purpose.
Helper for scanning for files with a given name.
uint Scan(std::string_view extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Scanner to scan for a particular type of FIOS file.
FiosFileScanner(SaveLoadOperation fop, FiosGetTypeAndNameProc *callback_proc, FileList &file_list)
Create the scanner.
SaveLoadOperation fop
The kind of file we are looking for.
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.
FileList & file_list
Destination of the found files.
FiosGetTypeAndNameProc * callback_proc
Callback to check whether the file may be added.
Scanner to find the unique IDs of scenarios.
bool scanned
Whether we've already scanned.
void Scan(bool rescan)
Scan, but only if it's needed.
bool AddFile(const std::string &filename, size_t, const std::string &) override
Add a file with the given filename.
ScenarioScanner()
Initialise.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
bool FioRemove(const std::string &filename)
Remove a file.
std::optional< FileHandle > FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Functions for Standard In/Out file operations.
SaveLoadOperation
Operation performed on the file.
@ SLO_SAVE
File is being saved.
@ SLO_LOAD
File is being loaded.
FiosType
Elements of a file system that are recognized.
Searchpath
Types of searchpaths OpenTTD might use.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ NO_DIRECTORY
A path without any base directory.
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
@ SCENARIO_DIR
Base directory for all scenarios.
@ SAVE_DIR
Base directory for all savegames.
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
AbstractFileType
The different abstract types of files that the system knows about.
@ FT_SCENARIO
old or new scenario
@ FT_HEIGHTMAP
heightmap file
@ FT_SAVEGAME
old or new savegame
@ FT_TOWN_DATA
town data file
bool HasScenario(const ContentInfo *ci, bool md5sum)
Check whether we've got a given scenario based on its unique ID.
static std::string GetFileTitle(const std::string &file, 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...
static std::tuple< FiosType, std::string > FiosGetTownDataListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetTownDataList.
const char * FindScenario(const ContentInfo *ci, bool md5sum)
Find a given scenario based on its unique ID.
std::string FiosGetCurrentPath()
Get the current path/working directory.
std::string FiosMakeSavegameName(const char *name)
Make a save game or scenario filename from a name.
void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of savegames.
void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of heightmaps.
static void FiosGetFileList(SaveLoadOperation fop, bool show_dirs, FiosGetTypeAndNameProc *callback_proc, Subdirectory subdir, FileList &file_list)
Fill the list of the files in a directory, according to some arbitrary rule.
void ScanScenarios()
Force a (re)scan of the scenarios.
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of scenarios.
static std::string FiosMakeFilename(const std::string *path, const char *name, const char *ext)
Construct a filename from its components in destination buffer buf.
std::tuple< FiosType, std::string > FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetFileList.
std::tuple< FiosType, std::string > FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetFileList.
std::string FiosMakeHeightmapName(const char *name)
Construct a filename for a height map.
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
bool FiosDelete(const char *name)
Delete a file.
bool FiosBrowseTo(const FiosItem *item)
Browse to a new path based on the passed item, starting at _fios_path.
static ScenarioScanner _scanner
Scanner for scenarios.
void FiosGetTownDataList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of town data files.
Declarations for savegames operations.
void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of savegames.
void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of heightmaps.
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of scenarios.
void FiosGetTownDataList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of town data files.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Part of the network protocol handling content distribution.
A number of safeguards to prevent using unsafe methods.
std::string_view GetCurrentScreenshotExtension()
Get filename extension of current screenshot file format.
Functions to make screenshots.
ClientSettings _settings_client
The current settings for this game.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
bool StrEqualsIgnoreCase(const std::string_view str1, const std::string_view str2)
Compares two string( view)s for equality, while ignoring the case of the characters.
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Functions related to low-level strings.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Functions related to OTTD's strings.
GUISettings gui
settings related to the GUI
Container for all important information about a piece of content.
uint32_t unique_id
Unique ID; either GRF ID or shortname.
MD5Hash md5sum
The MD5 checksum.
Deals with finding savegames.
bool operator<(const FiosItem &other) const
Compare two FiosItem's.
std::string Filename()
Generate a savegame name and number according to _settings_client.gui.max_num_autosaves.
FiosNumberedSaveName(const std::string &prefix)
Constructs FiosNumberedSaveName.
std::string Extension()
Generate an extension for a savegame name.
uint8_t max_num_autosaves
controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num...
Basic data to distinguish a scenario.
uint32_t scenid
ID for the scenario (generated by content).
MD5Hash md5sum
MD5 checksum of file.
std::string filename
filename of the file.
Structs, typedefs and macros used for TAR file handling.
std::wstring OTTD2FS(const std::string &name)
Convert from OpenTTD's encoding to a wide string.
std::string FS2OTTD(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.