14 #include "3rdparty/md5/md5.h"
26 #include "table/strings.h"
31 static std::string *_fios_path =
nullptr;
32 SortingBits _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
35 extern bool FiosIsRoot(
const std::string &path);
36 extern bool FiosIsHiddenFile(
const std::filesystem::path &path);
37 extern void FiosGetDrives(
FileList &file_list);
40 extern 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:
201 static 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(
".");
253 typedef 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;
343 fios.title =
GetString(STR_SAVELOAD_PARENT_DIRECTORY);
344 sort_start = file_list.size();
348 std::error_code error_code;
349 for (
const auto &dir_entry : std::filesystem::directory_iterator(
OTTD2FS(*_fios_path), error_code)) {
350 if (!dir_entry.is_directory())
continue;
351 if (FiosIsHiddenFile(dir_entry) && dir_entry.path().filename() != PERSONAL_DIR)
continue;
353 FiosItem &fios = file_list.emplace_back();
354 fios.type = FIOS_TYPE_DIR;
356 fios.name =
FS2OTTD(dir_entry.path().filename());
358 fios.title =
GetString(STR_SAVELOAD_DIRECTORY);
362 SortingBits order = _savegame_sort_order;
363 _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
364 std::sort(file_list.begin() + sort_start, file_list.end());
365 _savegame_sort_order = order;
369 sort_start = file_list.size();
374 scanner.
Scan({}, *_fios_path,
false);
376 scanner.
Scan({}, subdir,
true,
true);
379 std::sort(file_list.begin() + sort_start, file_list.end());
382 FiosGetDrives(file_list);
395 if (!f.has_value())
return {};
398 size_t read = fread(title, 1,
lengthof(title), *f);
428 return { FIOS_TYPE_OLDFILE, GetOldSaveGameName(file) };
432 return { FIOS_TYPE_INVALID, {} };
444 static std::optional<std::string> fios_save_path;
446 if (!fios_save_path) fios_save_path = FioFindDirectory(
SAVE_DIR);
448 _fios_path = &(*fios_save_path);
475 return { FIOS_TYPE_OLD_SCENARIO, GetOldSaveGameName(file) };
479 return { FIOS_TYPE_INVALID, {} };
491 static std::optional<std::string> fios_scn_path;
494 if (!fios_scn_path) fios_scn_path = FioFindDirectory(
SCENARIO_DIR);
496 _fios_path = &(*fios_scn_path);
503 std::tuple<FiosType, std::string> FiosGetHeightmapListCallback(
SaveLoadOperation,
const std::string &file,
const std::string_view ext)
518 if (type == FIOS_TYPE_INVALID)
return { FIOS_TYPE_INVALID, {} };
520 TarFileList::iterator it = _tar_filelist[
SCENARIO_DIR].find(file);
531 if (buf.compare(0, buf.size(), it->second.tar_filename, 0, buf.size()) == 0) {
537 if (!match)
return { FIOS_TYPE_INVALID, {} };
551 static std::optional<std::string> fios_hmap_path;
553 if (!fios_hmap_path) fios_hmap_path = FioFindDirectory(
HEIGHTMAP_DIR);
555 _fios_path = &(*fios_hmap_path);
559 FiosGetFileList(fop, show_dirs, &FiosGetHeightmapListCallback, subdir, file_list);
576 return { FIOS_TYPE_INVALID, {} };
587 static std::optional<std::string> fios_town_data_path;
589 if (!fios_town_data_path) fios_town_data_path = FioFindDirectory(
HEIGHTMAP_DIR);
591 _fios_path = &(*fios_town_data_path);
604 static std::optional<std::string> fios_screenshot_path;
606 if (!fios_screenshot_path) fios_screenshot_path = FioFindDirectory(
SCREENSHOT_DIR);
608 return fios_screenshot_path->c_str();
619 return this->scenid == other.
scenid && this->md5sum == other.
md5sum;
624 return !(*
this == other);
643 if (this->scanned && !rescan)
return;
646 this->scanned =
true;
649 bool AddFile(
const std::string &filename,
size_t,
const std::string &)
override
652 if (!f.has_value())
return false;
655 int fret = fscanf(*f,
"%u", &
id.scenid);
656 if (fret != 1)
return false;
657 id.filename = filename;
660 uint8_t buffer[1024];
667 if (!f.has_value())
return false;
670 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, *f)) != 0 && size != 0) {
672 checksum.Append(buffer, len);
674 checksum.Finish(
id.md5sum);
695 if (md5sum ? (
id.md5sum == ci->
md5sum)
697 return id.filename.c_str();
729 static std::optional<std::string> _autosave_path;
730 if (!_autosave_path) _autosave_path = FioFindDirectory(
AUTOSAVE_DIR);
732 static std::string _prefix;
735 static FiosGetTypeAndNameProc *proc = [](
SaveLoadOperation,
const std::string &file,
const std::string_view ext) {
736 if (
StrEqualsIgnoreCase(ext,
".sav") && file.starts_with(_prefix))
return std::tuple(FIOS_TYPE_FILE, std::string{});
737 return std::tuple(FIOS_TYPE_INVALID, std::string{});
741 _prefix = *_autosave_path + this->prefix;
746 scanner.
Scan(
".sav", *_autosave_path,
false);
749 if (list.begin() != list.end()) {
750 SortingBits order = _savegame_sort_order;
751 _savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;
752 std::sort(list.begin(), list.end());
753 _savegame_sort_order = order;
755 std::string_view name = list.begin()->title;
756 std::from_chars(name.data() + this->prefix.size(), name.data() + name.size(), this->number);
767 return fmt::format(
"{}{}.sav", this->prefix, this->number);
776 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.
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
FiosType
Elements of a file system that are recognized.
SaveLoadOperation
Operation performed on the file.
@ SLO_SAVE
File is being saved.
@ SLO_LOAD
File is being loaded.
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.
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...
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.
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
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::string FiosMakeHeightmapName(const char *name)
Construct a filename for a height map.
std::tuple< FiosType, std::string > FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetFileList.
bool FiosDelete(const char *name)
Delete a file.
std::tuple< FiosType, std::string > FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetFileList.
static std::tuple< FiosType, std::string > FiosGetTownDataListCallback(SaveLoadOperation fop, const std::string &file, const std::string_view ext)
Callback for FiosGetTownDataList.
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.
constexpr bool IsInsideMM(const 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.
const char * 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 all the associated DParam lookups and formatting.
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
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.