11#include "3rdparty/md5/md5.h"
24#include "table/strings.h"
28static std::string *_fios_path =
nullptr;
31extern bool FiosIsRoot(
const std::string &path);
32extern bool FiosIsHiddenFile(
const std::filesystem::path &path);
33extern void FiosGetDrives(
FileList &file_list);
36extern std::string GetOldSaveGameName(std::string_view file);
48 return a.mtime < b.mtime;
62 switch (abstract_filetype) {
95 for (
const auto &it : *
this) {
97 if (file == item->name)
return item;
104 if (number.has_value() && !consumer.
AnyBytesLeft() &&
IsInsideMM(*number, 0, this->size()))
return &this->at(*number);
108 std::string long_file(file);
110 for (
const auto &it : *
this) {
112 if (long_file == item->name)
return item;
136 assert(_fios_path !=
nullptr);
139 std::string_view base = *_fios_path;
140 if (!base.empty() && base.back() == PATHSEPCHAR) base.remove_suffix(1);
142 std::filesystem::path target =
OTTD2FS(fmt::format(
"{}{}{}", base, PATHSEP, name));
144 std::error_code error_code;
145 if (std::filesystem::create_directory(target, error_code)) {
170 assert(_fios_path !=
nullptr);
171 *_fios_path = std::string{ item->name, 0, 1 } +
":" PATHSEP;
179 assert(_fios_path !=
nullptr);
180 auto s = _fios_path->find_last_of(PATHSEPCHAR);
181 if (s != std::string::npos && s != 0) {
182 _fios_path->erase(s);
185 s = _fios_path->find_last_of(PATHSEPCHAR);
186 if (s != std::string::npos) {
187 _fios_path->erase(s + 1);
193 assert(_fios_path !=
nullptr);
194 *_fios_path += item->name;
195 *_fios_path += PATHSEP;
199 assert(_fios_path !=
nullptr);
200 *_fios_path = item->name;
217static std::string
FiosMakeFilename(
const std::string *path, std::string_view name, std::string_view ext)
219 std::string_view base_path;
221 if (path !=
nullptr) {
224 if (!base_path.empty() && base_path.back() == PATHSEPCHAR) base_path.remove_suffix(1);
228 auto period = name.find_last_of(
'.');
229 if (period != std::string_view::npos &&
StrEqualsIgnoreCase(name.substr(period), ext)) ext =
"";
231 return fmt::format(
"{}{}{}{}", base_path, PATHSEP, name, ext);
241 std::string_view extension = (_game_mode ==
GameMode::Editor) ?
".scn" :
".sav";
256typedef std::tuple<FiosType, std::string> FiosGetTypeAndNameProc(
SaveLoadOperation fop, std::string_view filename, std::string_view ext);
276 bool AddFile(
const std::string &filename,
size_t,
const std::string &)
override;
286 auto sep = filename.rfind(
'.');
287 if (sep == std::string::npos)
return false;
288 std::string ext = filename.substr(sep);
291 if (type == FIOS_TYPE_INVALID)
return false;
294 if (filename == fios.name)
return false;
299 std::error_code error_code;
300 auto write_time = std::filesystem::last_write_time(
OTTD2FS(filename), error_code);
304 fios->mtime = std::chrono::duration_cast<std::chrono::milliseconds>(write_time.time_since_epoch()).count();
308 fios->name = filename;
312 auto ps = filename.rfind(PATHSEPCHAR);
332 size_t sort_start = 0;
336 assert(_fios_path !=
nullptr);
340 if (!FiosIsRoot(*_fios_path)) {
341 FiosItem &fios = file_list.emplace_back();
342 fios.type = FIOS_TYPE_PARENT;
346 sort_start = file_list.size();
350 std::error_code error_code;
351 for (
const auto &dir_entry : std::filesystem::directory_iterator(
OTTD2FS(*_fios_path), error_code)) {
352 if (!dir_entry.is_directory())
continue;
353 if (FiosIsHiddenFile(dir_entry) && dir_entry.path().filename() != PERSONAL_DIR)
continue;
355 FiosItem &fios = file_list.emplace_back();
356 fios.type = FIOS_TYPE_DIR;
358 fios.name =
FS2OTTD(dir_entry.path().filename().native());
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(),
FiosItemSorter);
380 FiosGetDrives(file_list);
392 std::string filename = fmt::format(
"{}.title", file);
394 if (!f.has_value())
return {};
397 size_t read = fread(title, 1,
lengthof(title), *f);
427 return { FIOS_TYPE_OLDFILE, GetOldSaveGameName(file) };
431 return { FIOS_TYPE_INVALID, {} };
443 static std::optional<std::string> fios_save_path;
447 _fios_path = &(*fios_save_path);
474 return { FIOS_TYPE_OLD_SCENARIO, GetOldSaveGameName(file) };
478 return { FIOS_TYPE_INVALID, {} };
490 static std::optional<std::string> fios_scn_path;
495 _fios_path = &(*fios_scn_path);
502std::tuple<FiosType, std::string> FiosGetHeightmapListCallback(
SaveLoadOperation, std::string_view file, std::string_view ext)
517 if (type == FIOS_TYPE_INVALID)
return { FIOS_TYPE_INVALID, {} };
530 if (it->second.tar_filename.starts_with(buf)) {
536 if (!match)
return { FIOS_TYPE_INVALID, {} };
550 static std::optional<std::string> fios_hmap_path;
554 _fios_path = &(*fios_hmap_path);
558 FiosGetFileList(fop, show_dirs, &FiosGetHeightmapListCallback, subdir, file_list);
576 return { FIOS_TYPE_INVALID, {} };
587 static std::optional<std::string> fios_town_data_path;
591 _fios_path = &(*fios_town_data_path);
604 static std::optional<std::string> fios_screenshot_path;
608 return *fios_screenshot_path;
619 return this->scenid == other.
scenid && this->md5sum == other.
md5sum;
638 if (this->scanned && !rescan)
return;
641 this->scanned =
true;
644 bool AddFile(
const std::string &filename,
size_t,
const std::string &)
override
647 if (!f.has_value())
return false;
650 int fret = fscanf(*f,
"%u", &
id.scenid);
651 if (fret != 1)
return false;
652 id.filename = filename;
655 uint8_t buffer[1024];
662 if (!f.has_value())
return false;
665 while ((len = fread(buffer, 1, (size >
sizeof(buffer)) ?
sizeof(buffer) : size, *f)) != 0 && size != 0) {
667 checksum.Append(buffer, len);
669 checksum.Finish(
id.md5sum);
690 if (md5sum ? (
id.md5sum == ci.
md5sum)
724 static std::optional<std::string> _autosave_path;
727 static std::string _prefix;
730 static FiosGetTypeAndNameProc *
const proc = [](
SaveLoadOperation, std::string_view file, std::string_view ext) {
731 if (
StrEqualsIgnoreCase(ext,
".sav") && file.starts_with(_prefix))
return std::tuple(FIOS_TYPE_FILE, std::string{});
732 return std::tuple(FIOS_TYPE_INVALID, std::string{});
736 _prefix = *_autosave_path + this->prefix;
741 scanner.
Scan(
".sav", *_autosave_path,
false);
746 std::string name = elem->title.GetDecodedString();
747 std::from_chars(name.data() + this->prefix.size(), name.data() + name.size(), this->number);
757 if (++this->number >=
_settings_client.gui.max_num_autosaves) this->number = 0;
758 return fmt::format(
"{}{}.sav", this->prefix, this->number);
767 return fmt::format(
"-{}.sav", this->prefix);
std::string GetDecodedString() const
Decode the encoded string.
List of file information.
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs)
Construct a file list with the given kind of files, for the stated purpose.
const FiosItem * FindItem(std::string_view file)
Find file information of a file by its name from the file list.
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.
FileList & file_list
Destination of the found files.
bool AddFile(const std::string &filename, size_t, const std::string &) override
Try to add a fios item set with the given filename.
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.
Parse data from a string / buffer.
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
EnumIndexArray< TarFileList, Subdirectory, Subdirectory::End > _tar_filelist
List of files within tar files found in each subdirectory.
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view 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.
@ Save
File is being saved.
@ Load
File is being loaded.
@ FiosDirect
Direct filename.
@ FiosDrive
A drive (letter) entry.
@ Invalid
Unknown or invalid file.
@ FiosDirectory
A directory entry.
@ FiosParent
A parent directory entry.
Searchpath
Types of searchpaths OpenTTD might use.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ Autosave
Subdirectory of save for autosaves.
@ Screenshot
Subdirectory for all screenshots.
@ Scenario
Base directory for all scenarios.
@ Heightmap
Subdirectory of scenario for heightmaps.
@ None
A path without any base directory.
@ Save
Base directory for all savegames.
AbstractFileType
The different abstract types of files that the system knows about.
@ Savegame
old or new savegame
@ Scenario
old or new scenario
@ Heightmap
heightmap file
std::tuple< FiosType, std::string > FiosGetScenarioListCallback(SaveLoadOperation fop, std::string_view file, std::string_view ext)
Callback for FiosGetFileList.
bool FiosItemModificationDateSorter(const FiosItem &a, const FiosItem &b)
Sort files by their modification date, and name when they are equal.
static std::tuple< FiosType, std::string > FiosGetTownDataListCallback(SaveLoadOperation fop, std::string_view file, std::string_view ext)
Callback for FiosGetTownDataList.
std::optional< std::string_view > FindScenario(const ContentInfo &ci, bool md5sum)
Find a given scenario based on its unique ID.
std::string FiosMakeSavegameName(std::string_view name)
Make a save game or scenario filename from a name.
bool FiosItemNameSorter(const FiosItem &a, const FiosItem &b)
Sort files by their name.
std::tuple< FiosType, std::string > FiosGetSavegameListCallback(SaveLoadOperation fop, std::string_view file, std::string_view ext)
Callback for FiosGetFileList.
static std::string GetFileTitle(std::string_view 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...
std::string FiosGetCurrentPath()
Get the current path/working directory.
static std::string FiosMakeFilename(const std::string *path, std::string_view name, std::string_view ext)
Construct a filename from its components in destination buffer buf.
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.
std::string FiosMakeHeightmapName(std::string_view name)
Construct a filename for a height map.
bool HasScenario(const ContentInfo &ci, bool md5sum)
Check whether we've got a given scenario based on its unique ID.
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
Get a list of scenarios.
std::string_view FiosGetScreenshotDir()
Get the directory for screenshots.
DirectoryCreateResult FiosCreateDirectory(std::string_view name)
Create a new subdirectory inside the current FIOS path.
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.
bool FiosItemSorter(const FiosItem &a, const FiosItem &b)
Sorts the FiosItems based on the savegame sorter and order.
DirectoryCreateResult
Outcome of a directory creation attempt.
@ PermissionDenied
The OS rejected the operation for permission reasons.
@ Success
Directory was created.
@ OtherError
Any other filesystem error.
@ AlreadyExists
A file or directory with that name already exists.
bool FiosItemModificationDateSorter(const FiosItem &a, const FiosItem &b)
Sort files by their modification date, and name when they are equal.
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.
@ Editor
In the scenario editor.
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.
bool StrEqualsIgnoreCase(std::string_view str1, 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.
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Functions related to low-level strings.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Functions related to OTTD's strings.
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.
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.
Elements of a file system that are recognized.
DetailedFileType detailed
Detailed file type.
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(std::string_view name)
Convert from OpenTTD's encoding to a wide string.
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.