diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index d47e0cdc5a..6609ef91db 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -56,9 +56,8 @@ static uint _script_current_depth; ///< Depth of scripts running (used to abort /** File list storage for the console, for caching the last 'ls' command. */ class ConsoleFileList : public FileList { public: - ConsoleFileList() : FileList() + ConsoleFileList(AbstractFileType abstract_filetype, bool show_dirs) : FileList(), abstract_filetype(abstract_filetype), show_dirs(show_dirs) { - this->file_list_valid = false; } /** Declare the file storage cache as being invalid, also clears all stored files. */ @@ -75,15 +74,19 @@ public: void ValidateFileList(bool force_reload = false) { if (force_reload || !this->file_list_valid) { - this->BuildFileList(FT_SAVEGAME, SLO_LOAD); + this->BuildFileList(this->abstract_filetype, SLO_LOAD, this->show_dirs); this->file_list_valid = true; } } - bool file_list_valid; ///< If set, the file list is valid. + AbstractFileType abstract_filetype; ///< The abstract file type to list. + bool show_dirs; ///< Whether to show directories in the file list. + bool file_list_valid = false; ///< If set, the file list is valid. }; -static ConsoleFileList _console_file_list; ///< File storage cache for the console. +static ConsoleFileList _console_file_list_savegame{FT_SAVEGAME, true}; ///< File storage cache for savegames. +static ConsoleFileList _console_file_list_scenario{FT_SCENARIO, false}; ///< File storage cache for scenarios. +static ConsoleFileList _console_file_list_heightmap{FT_HEIGHTMAP, false}; ///< File storage cache for heightmaps. /* console command defines */ #define DEF_CONSOLE_CMD(function) static bool function([[maybe_unused]] byte argc, [[maybe_unused]] char *argv[]) @@ -424,8 +427,8 @@ DEF_CONSOLE_CMD(ConLoad) if (argc != 2) return false; const char *file = argv[1]; - _console_file_list.ValidateFileList(); - const FiosItem *item = _console_file_list.FindItem(file); + _console_file_list_savegame.ValidateFileList(); + const FiosItem *item = _console_file_list_savegame.FindItem(file); if (item != nullptr) { if (GetAbstractFileType(item->type) == FT_SAVEGAME) { _switch_mode = SM_LOAD_GAME; @@ -440,6 +443,57 @@ DEF_CONSOLE_CMD(ConLoad) return true; } +DEF_CONSOLE_CMD(ConLoadScenario) +{ + if (argc == 0) { + IConsolePrint(CC_HELP, "Load a scenario by name or index. Usage: 'load_scenario '."); + return true; + } + + if (argc != 2) return false; + + const char *file = argv[1]; + _console_file_list_scenario.ValidateFileList(); + const FiosItem *item = _console_file_list_scenario.FindItem(file); + if (item != nullptr) { + if (GetAbstractFileType(item->type) == FT_SCENARIO) { + _switch_mode = SM_LOAD_GAME; + _file_to_saveload.Set(*item); + } else { + IConsolePrint(CC_ERROR, "'{}' is not a scenario.", file); + } + } else { + IConsolePrint(CC_ERROR, "'{}' cannot be found.", file); + } + + return true; +} + +DEF_CONSOLE_CMD(ConLoadHeightmap) +{ + if (argc == 0) { + IConsolePrint(CC_HELP, "Load a heightmap by name or index. Usage: 'load_heightmap '."); + return true; + } + + if (argc != 2) return false; + + const char *file = argv[1]; + _console_file_list_heightmap.ValidateFileList(); + const FiosItem *item = _console_file_list_heightmap.FindItem(file); + if (item != nullptr) { + if (GetAbstractFileType(item->type) == FT_HEIGHTMAP) { + _switch_mode = SM_START_HEIGHTMAP; + _file_to_saveload.Set(*item); + } else { + IConsolePrint(CC_ERROR, "'{}' is not a heightmap.", file); + } + } else { + IConsolePrint(CC_ERROR, "'{}' cannot be found.", file); + } + + return true; +} DEF_CONSOLE_CMD(ConRemove) { @@ -451,8 +505,8 @@ DEF_CONSOLE_CMD(ConRemove) if (argc != 2) return false; const char *file = argv[1]; - _console_file_list.ValidateFileList(); - const FiosItem *item = _console_file_list.FindItem(file); + _console_file_list_savegame.ValidateFileList(); + const FiosItem *item = _console_file_list_savegame.FindItem(file); if (item != nullptr) { if (unlink(item->name.c_str()) != 0) { IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name); @@ -461,7 +515,7 @@ DEF_CONSOLE_CMD(ConRemove) IConsolePrint(CC_ERROR, "'{}' could not be found.", file); } - _console_file_list.InvalidateFileList(); + _console_file_list_savegame.InvalidateFileList(); return true; } @@ -474,9 +528,41 @@ DEF_CONSOLE_CMD(ConListFiles) return true; } - _console_file_list.ValidateFileList(true); - for (uint i = 0; i < _console_file_list.size(); i++) { - IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list[i].title); + _console_file_list_savegame.ValidateFileList(true); + for (uint i = 0; i < _console_file_list_savegame.size(); i++) { + IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_savegame[i].title); + } + + return true; +} + +/* List all the scenarios */ +DEF_CONSOLE_CMD(ConListScenarios) +{ + if (argc == 0) { + IConsolePrint(CC_HELP, "List all loadable scenarios. Usage: 'list_scenarios'."); + return true; + } + + _console_file_list_scenario.ValidateFileList(true); + for (uint i = 0; i < _console_file_list_scenario.size(); i++) { + IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_scenario[i].title); + } + + return true; +} + +/* List all the heightmaps */ +DEF_CONSOLE_CMD(ConListHeightmaps) +{ + if (argc == 0) { + IConsolePrint(CC_HELP, "List all loadable heightmaps. Usage: 'list_heightmaps'."); + return true; + } + + _console_file_list_heightmap.ValidateFileList(true); + for (uint i = 0; i < _console_file_list_heightmap.size(); i++) { + IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_heightmap[i].title); } return true; @@ -493,8 +579,8 @@ DEF_CONSOLE_CMD(ConChangeDirectory) if (argc != 2) return false; const char *file = argv[1]; - _console_file_list.ValidateFileList(true); - const FiosItem *item = _console_file_list.FindItem(file); + _console_file_list_savegame.ValidateFileList(true); + const FiosItem *item = _console_file_list_savegame.FindItem(file); if (item != nullptr) { switch (item->type) { case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT: @@ -506,7 +592,7 @@ DEF_CONSOLE_CMD(ConChangeDirectory) IConsolePrint(CC_ERROR, "{}: No such file or directory.", file); } - _console_file_list.InvalidateFileList(); + _console_file_list_savegame.InvalidateFileList(); return true; } @@ -518,8 +604,8 @@ DEF_CONSOLE_CMD(ConPrintWorkingDirectory) } /* XXX - Workaround for broken file handling */ - _console_file_list.ValidateFileList(true); - _console_file_list.InvalidateFileList(); + _console_file_list_savegame.ValidateFileList(true); + _console_file_list_savegame.InvalidateFileList(); IConsolePrint(CC_DEFAULT, FiosGetCurrentPath()); return true; @@ -2531,10 +2617,16 @@ void IConsoleStdLibRegister() IConsole::CmdRegister("scrollto", ConScrollToTile); IConsole::CmdRegister("alias", ConAlias); IConsole::CmdRegister("load", ConLoad); + IConsole::CmdRegister("load_save", ConLoad); + IConsole::CmdRegister("load_scenario", ConLoadScenario); + IConsole::CmdRegister("load_heightmap", ConLoadHeightmap); IConsole::CmdRegister("rm", ConRemove); IConsole::CmdRegister("save", ConSave); IConsole::CmdRegister("saveconfig", ConSaveConfig); IConsole::CmdRegister("ls", ConListFiles); + IConsole::CmdRegister("list_saves", ConListFiles); + IConsole::CmdRegister("list_scenarios", ConListScenarios); + IConsole::CmdRegister("list_heightmaps", ConListHeightmaps); IConsole::CmdRegister("cd", ConChangeDirectory); IConsole::CmdRegister("pwd", ConPrintWorkingDirectory); IConsole::CmdRegister("clear", ConClearBuffer); diff --git a/src/fios.cpp b/src/fios.cpp index 48e0466eea..ec1f3de3b2 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -65,8 +65,9 @@ bool FiosItem::operator< (const FiosItem &other) const * Construct a file list with the given kind of files, for the stated purpose. * @param abstract_filetype Kind of files to collect. * @param fop Purpose of the collection, either #SLO_LOAD or #SLO_SAVE. + * @param show_dirs Whether to show directories. */ -void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop) +void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs) { this->clear(); @@ -76,15 +77,15 @@ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperati break; case FT_SAVEGAME: - FiosGetSavegameList(fop, *this); + FiosGetSavegameList(fop, show_dirs, *this); break; case FT_SCENARIO: - FiosGetScenarioList(fop, *this); + FiosGetScenarioList(fop, show_dirs, *this); break; case FT_HEIGHTMAP: - FiosGetHeightmapList(fop, *this); + FiosGetHeightmapList(fop, show_dirs, *this); break; default: @@ -337,11 +338,12 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t, const std::st /** * Fill the list of the files in a directory, according to some arbitrary rule. * @param fop Purpose of collecting the list. + * @param show_dirs Whether to list directories. * @param callback_proc The function that is called where you need to do the filtering. * @param subdir The directory from where to start (global) searching. * @param file_list Destination of the found files. */ -static void FiosGetFileList(SaveLoadOperation fop, FiosGetTypeAndNameProc *callback_proc, Subdirectory subdir, FileList &file_list) +static void FiosGetFileList(SaveLoadOperation fop, bool show_dirs, FiosGetTypeAndNameProc *callback_proc, Subdirectory subdir, FileList &file_list) { struct stat sb; struct dirent *dirent; @@ -354,7 +356,7 @@ static void FiosGetFileList(SaveLoadOperation fop, FiosGetTypeAndNameProc *callb assert(_fios_path != nullptr); /* A parent directory link exists if we are not in the root directory */ - if (!FiosIsRoot(*_fios_path)) { + if (show_dirs && !FiosIsRoot(*_fios_path)) { fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; @@ -364,7 +366,7 @@ static void FiosGetFileList(SaveLoadOperation fop, FiosGetTypeAndNameProc *callb } /* Show subdirectories */ - if ((dir = ttd_opendir(_fios_path->c_str())) != nullptr) { + if (show_dirs && (dir = ttd_opendir(_fios_path->c_str())) != nullptr) { while ((dirent = readdir(dir)) != nullptr) { std::string d_name = FS2OTTD(dirent->d_name); @@ -384,7 +386,7 @@ static void FiosGetFileList(SaveLoadOperation fop, FiosGetTypeAndNameProc *callb } /* Sort the subdirs always by name, ascending, remember user-sorting order */ - { + if (show_dirs) { SortingBits order = _savegame_sort_order; _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING; std::sort(file_list.begin(), file_list.end()); @@ -464,10 +466,11 @@ std::tuple FiosGetSavegameListCallback(SaveLoadOperation /** * Get a list of savegames. * @param fop Purpose of collecting the list. + * @param show_dirs Whether to show directories. * @param file_list Destination of the found files. * @see FiosGetFileList */ -void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list) +void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list) { static std::optional fios_save_path; @@ -475,7 +478,7 @@ void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list) _fios_path = &(*fios_save_path); - FiosGetFileList(fop, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list); + FiosGetFileList(fop, show_dirs, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list); } /** @@ -510,10 +513,11 @@ std::tuple FiosGetScenarioListCallback(SaveLoadOperation /** * Get a list of scenarios. * @param fop Purpose of collecting the list. + * @param show_dirs Whether to show directories. * @param file_list Destination of the found files. * @see FiosGetFileList */ -void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list) +void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list) { static std::optional fios_scn_path; @@ -524,7 +528,7 @@ void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list) std::string base_path = FioFindDirectory(SCENARIO_DIR); Subdirectory subdir = (fop == SLO_LOAD && base_path == *_fios_path) ? SCENARIO_DIR : NO_DIRECTORY; - FiosGetFileList(fop, &FiosGetScenarioListCallback, subdir, file_list); + FiosGetFileList(fop, show_dirs, &FiosGetScenarioListCallback, subdir, file_list); } std::tuple FiosGetHeightmapListCallback(SaveLoadOperation, const std::string &file, const std::string_view ext) @@ -570,9 +574,10 @@ std::tuple FiosGetHeightmapListCallback(SaveLoadOperation /** * Get a list of heightmaps. * @param fop Purpose of collecting the list. + * @param show_dirs Whether to show directories. * @param file_list Destination of the found files. */ -void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list) +void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list) { static std::optional fios_hmap_path; @@ -582,7 +587,7 @@ void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list) std::string base_path = FioFindDirectory(HEIGHTMAP_DIR); Subdirectory subdir = base_path == *_fios_path ? HEIGHTMAP_DIR : NO_DIRECTORY; - FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list); + FiosGetFileList(fop, show_dirs, &FiosGetHeightmapListCallback, subdir, file_list); } /** diff --git a/src/fios.h b/src/fios.h index 5827538e46..0bfc9f7640 100644 --- a/src/fios.h +++ b/src/fios.h @@ -87,7 +87,7 @@ struct FiosItem { /** List of file information. */ class FileList : public std::vector { public: - void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop); + void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs); const FiosItem *FindItem(const std::string_view file); }; @@ -104,9 +104,9 @@ extern SortingBits _savegame_sort_order; void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop); -void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list); -void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list); -void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list); +void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list); +void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list); +void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list); bool FiosBrowseTo(const FiosItem *item); diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 26b182184c..041189711f 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -829,7 +829,7 @@ public: if (!gui_scope) break; _fios_path_changed = true; - this->fios_items.BuildFileList(this->abstract_filetype, this->fop); + this->fios_items.BuildFileList(this->abstract_filetype, this->fop, true); this->selected = nullptr; _load_check_data.Clear();