diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index 3a2a6ec007..a7d07e6335 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -134,14 +134,15 @@ static std::vector _listItems; static char _directory[MAX_PATH]; static char _shortenedDirectory[MAX_PATH]; static char _parentDirectory[MAX_PATH]; -static char _extension[256]; -static std::string _defaultPath; +static u8string _extensionPattern; +static u8string _defaultPath; static int32_t _type; static int32_t maxDateWidth = 0; static int32_t maxTimeWidth = 0; -static void WindowLoadsavePopulateList(rct_window* w, int32_t includeNewItem, const utf8* directory, const char* extension); +static void WindowLoadsavePopulateList( + rct_window* w, int32_t includeNewItem, u8string_view directory, std::string_view extensionPattern); static void WindowLoadsaveSelect(rct_window* w, const utf8* path); static void WindowLoadsaveSortList(); @@ -227,6 +228,15 @@ static const char* GetFilterPatternByType(const int32_t type, const bool isSave) return ""; } +static u8string RemovePatternWildcard(u8string_view pattern) +{ + while (pattern.length() >= 1 && pattern.front() == '*') + { + pattern.remove_prefix(1); + } + return u8string{ pattern }; +} + static u8string WindowLoadsaveGetDir(const int32_t type) { u8string result = GetLastDirectoryByType(type); @@ -414,16 +424,16 @@ static u8string Browse(bool isSave) desc.Title = language_get_string(title); - utf8 outPath[MAX_PATH]; - if (ContextOpenCommonFileDialog(outPath, desc, std::size(outPath))) + u8string outPath = ContextOpenCommonFileDialog(desc); + if (!outPath.empty()) { // When the given save type was given, Windows still interprets a filename with a dot in its name as a custom extension, // meaning files like "My Coaster v1.2" will not get the .td6 extension by default. if (isSave && get_file_extension_type(outPath) != fileType) - path_append_extension(outPath, extension.c_str(), std::size(outPath)); + outPath = Path::WithExtension(outPath, extension); } - return u8string(outPath); + return outPath; } static void WindowLoadsaveMouseup(rct_window* w, rct_widgetindex widgetIndex) @@ -436,7 +446,7 @@ static void WindowLoadsaveMouseup(rct_window* w, rct_widgetindex widgetIndex) break; case WIDX_UP: - WindowLoadsavePopulateList(w, isSave, _parentDirectory, _extension); + WindowLoadsavePopulateList(w, isSave, _parentDirectory, _extensionPattern); WindowInitScrollWidgets(w); w->no_list_items = static_cast(_listItems.size()); break; @@ -461,7 +471,7 @@ static void WindowLoadsaveMouseup(rct_window* w, rct_widgetindex widgetIndex) else { // If user cancels file dialog, refresh list - WindowLoadsavePopulateList(w, isSave, _directory, _extension); + WindowLoadsavePopulateList(w, isSave, _directory, _extensionPattern); WindowInitScrollWidgets(w); w->no_list_items = static_cast(_listItems.size()); } @@ -497,7 +507,7 @@ static void WindowLoadsaveMouseup(rct_window* w, rct_widgetindex widgetIndex) break; case WIDX_DEFAULT: - WindowLoadsavePopulateList(w, isSave, GetInitialDirectoryByType(_type).c_str(), _extension); + WindowLoadsavePopulateList(w, isSave, GetInitialDirectoryByType(_type).c_str(), _extensionPattern); WindowInitScrollWidgets(w); w->no_list_items = static_cast(_listItems.size()); break; @@ -529,7 +539,7 @@ static void WindowLoadsaveScrollmousedown(rct_window* w, int32_t scrollIndex, co char directory[MAX_PATH]; safe_strcpy(directory, _listItems[selectedItem].path.c_str(), sizeof(directory)); - WindowLoadsavePopulateList(w, includeNewItem, directory, _extension); + WindowLoadsavePopulateList(w, includeNewItem, directory, _extensionPattern); WindowInitScrollWidgets(w); w->no_list_items = static_cast(_listItems.size()); @@ -560,7 +570,6 @@ static void WindowLoadsaveScrollmouseover(rct_window* w, int32_t scrollIndex, co static void WindowLoadsaveTextinput(rct_window* w, rct_widgetindex widgetIndex, char* text) { - char path[MAX_PATH]; bool overwrite; if (text == nullptr || text[0] == 0) @@ -569,15 +578,14 @@ static void WindowLoadsaveTextinput(rct_window* w, rct_widgetindex widgetIndex, switch (widgetIndex) { case WIDX_NEW_FOLDER: + { if (!filename_valid_characters(text)) { context_show_error(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); return; } - safe_strcpy(path, _directory, sizeof(path)); - safe_strcat_path(path, text, sizeof(path)); - + const u8string path = Path::Combine(_directory, text); if (!Platform::EnsureDirectoryExists(path)) { context_show_error(STR_UNABLE_TO_CREATE_FOLDER, STR_NONE, {}); @@ -587,22 +595,23 @@ static void WindowLoadsaveTextinput(rct_window* w, rct_widgetindex widgetIndex, w->no_list_items = 0; w->selected_list_item = -1; - WindowLoadsavePopulateList(w, (_type & 1) == LOADSAVETYPE_SAVE, path, _extension); + WindowLoadsavePopulateList(w, (_type & 1) == LOADSAVETYPE_SAVE, path, _extensionPattern); WindowInitScrollWidgets(w); w->no_list_items = static_cast(_listItems.size()); w->Invalidate(); break; + } case WIDX_NEW_FILE: - safe_strcpy(path, _directory, sizeof(path)); - safe_strcat_path(path, text, sizeof(path)); - path_append_extension(path, _extension, sizeof(path)); + { + const u8string path = Path::WithExtension( + Path::Combine(_directory, text), RemovePatternWildcard(_extensionPattern)); overwrite = false; for (auto& item : _listItems) { - if (_stricmp(item.path.c_str(), path) == 0) + if (String::Equals(item.path, path)) { overwrite = true; break; @@ -610,10 +619,11 @@ static void WindowLoadsaveTextinput(rct_window* w, rct_widgetindex widgetIndex, } if (overwrite) - WindowOverwritePromptOpen(text, path); + WindowOverwritePromptOpen(text, path.c_str()); else - WindowLoadsaveSelect(w, path); + WindowLoadsaveSelect(w, path.c_str()); break; + } } } @@ -812,15 +822,13 @@ static void WindowLoadsaveSortList() std::sort(_listItems.begin(), _listItems.end(), ListItemSort); } -static void WindowLoadsavePopulateList(rct_window* w, int32_t includeNewItem, const utf8* directory, const char* extension) +static void WindowLoadsavePopulateList( + rct_window* w, int32_t includeNewItem, u8string_view directory, std::string_view extensionPattern) { const auto absoluteDirectory = Path::GetAbsolute(directory); safe_strcpy(_directory, absoluteDirectory.c_str(), std::size(_directory)); // Note: This compares the pointers, not values - if (_extension != extension) - { - safe_strcpy(_extension, extension, std::size(_extension)); - } + _extensionPattern = extensionPattern; _shortenedDirectory[0] = '\0'; _listItems.clear(); @@ -830,7 +838,7 @@ static void WindowLoadsavePopulateList(rct_window* w, int32_t includeNewItem, co window_loadsave_widgets[WIDX_NEW_FOLDER].type = includeNewItem ? WindowWidgetType::Button : WindowWidgetType::Empty; int32_t drives = Platform::GetDrives(); - if (str_is_null_or_empty(directory) && drives) + if (directory.empty() && drives) { // List Windows drives w->disabled_widgets |= (1ULL << WIDX_NEW_FILE) | (1ULL << WIDX_NEW_FOLDER) | (1ULL << WIDX_UP); @@ -901,17 +909,10 @@ static void WindowLoadsavePopulateList(rct_window* w, int32_t includeNewItem, co } // List all files with the wanted extensions - char filter[MAX_PATH]; - char extCopy[64]; - safe_strcpy(extCopy, extension, std::size(extCopy)); bool showExtension = false; - char* extToken = strtok(extCopy, ";"); - while (extToken != nullptr) + for (const u8string& extToken : String::Split(extensionPattern, ";")) { - safe_strcpy(filter, directory, std::size(filter)); - safe_strcat_path(filter, "*", std::size(filter)); - path_append_extension(filter, extToken, std::size(filter)); - + const u8string filter = Path::Combine(directory, extToken); auto scanner = Path::ScanDirectory(filter, false); while (scanner->Next()) { @@ -940,7 +941,6 @@ static void WindowLoadsavePopulateList(rct_window* w, int32_t includeNewItem, co _listItems.push_back(std::move(newListItem)); } - extToken = strtok(nullptr, ";"); showExtension = true; // Show any extension after the first iteration } diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 42eb803d9c..35b44e8853 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -1530,3 +1530,16 @@ bool ContextOpenCommonFileDialog(utf8* outFilename, OpenRCT2::Ui::FileDialogDesc return false; } } + +u8string ContextOpenCommonFileDialog(OpenRCT2::Ui::FileDialogDesc& desc) +{ + try + { + return GetContext()->GetUiContext()->ShowFileDialog(desc); + } + catch (const std::exception& ex) + { + log_error(ex.what()); + return u8string{}; + } +} diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index a35bc8758e..0d4dd72414 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -287,3 +287,4 @@ const utf8* context_get_path_legacy(int32_t pathId); bool context_load_park_from_file(const utf8* path); bool context_load_park_from_stream(void* stream); bool ContextOpenCommonFileDialog(utf8* outFilename, OpenRCT2::Ui::FileDialogDesc& desc, size_t outSize); +u8string ContextOpenCommonFileDialog(OpenRCT2::Ui::FileDialogDesc& desc); diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 74115e8d20..9baa6162ea 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -601,10 +601,7 @@ static void limit_autosave_count(const size_t numberOfFilesToKeep, bool processL fileFilter = "autosave_*.park"; } - utf8 filter[MAX_PATH]; - safe_strcpy(filter, folderDirectory.c_str(), sizeof(filter)); - safe_strcat_path(filter, "autosave", sizeof(filter)); - safe_strcat_path(filter, fileFilter, sizeof(filter)); + const u8string filter = Path::Combine(folderDirectory, "autosave", fileFilter); // At first, count how many autosaves there are { @@ -621,17 +618,14 @@ static void limit_autosave_count(const size_t numberOfFilesToKeep, bool processL return; } - auto autosaveFiles = std::vector(autosavesCount); + std::vector autosaveFiles; { auto scanner = Path::ScanDirectory(filter, false); for (size_t i = 0; i < autosavesCount; i++) { - autosaveFiles[i].resize(MAX_PATH, 0); if (scanner->Next()) { - safe_strcpy(autosaveFiles[i].data(), folderDirectory.c_str(), sizeof(utf8) * MAX_PATH); - safe_strcat_path(autosaveFiles[i].data(), "autosave", sizeof(utf8) * MAX_PATH); - safe_strcat_path(autosaveFiles[i].data(), scanner->GetPathRelative(), sizeof(utf8) * MAX_PATH); + autosaveFiles.emplace_back(Path::Combine(folderDirectory, "autosave", scanner->GetPathRelative())); } } } @@ -641,11 +635,11 @@ static void limit_autosave_count(const size_t numberOfFilesToKeep, bool processL }); // Calculate how many saves we need to delete. - numAutosavesToDelete = autosavesCount - numberOfFilesToKeep; + numAutosavesToDelete = autosaveFiles.size() - numberOfFilesToKeep; for (size_t i = 0; numAutosavesToDelete > 0; i++, numAutosavesToDelete--) { - if (!File::Delete(autosaveFiles[i].data())) + if (!File::Delete(autosaveFiles[i])) { log_warning("Failed to delete autosave file: %s", autosaveFiles[i].data()); } diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index cfe793ea19..9adad3b3dd 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -62,20 +62,6 @@ bool filename_valid_characters(const utf8* filename) return true; } -void path_append_extension(utf8* path, const utf8* newExtension, size_t size) -{ - // Skip to the dot if the extension starts with a pattern (starts with "*.") - if (newExtension[0] == '*') - newExtension++; - - // Append a dot to the filename if the new extension doesn't start with it - if (newExtension[0] != '.') - safe_strcat(path, ".", size); - - // Append the extension to the path - safe_strcat(path, newExtension, size); -} - void path_end_with_separator(utf8* path, size_t size) { size_t length = strnlen(path, size); @@ -410,16 +396,6 @@ char* safe_strcat(char* destination, const char* source, size_t size) return result; } -char* safe_strcat_path(char* destination, const char* source, size_t size) -{ - path_end_with_separator(destination, size); - if (source[0] == *PATH_SEPARATOR) - { - source = source + 1; - } - return safe_strcat(destination, source, size); -} - #if defined(_WIN32) char* strcasestr(const char* haystack, const char* needle) { diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index 5491f65ac4..ee1b19745b 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -25,7 +25,6 @@ int32_t mph_to_dmps(int32_t mph); bool filename_valid_characters(const utf8* filename); -void path_append_extension(utf8* path, const utf8* newExtension, size_t size); void path_end_with_separator(utf8* path, size_t size); bool sse41_available(); @@ -40,7 +39,6 @@ int32_t strlogicalcmp(char const* a, char const* b); utf8* safe_strtrunc(utf8* text, size_t size); char* safe_strcpy(char* destination, const char* source, size_t num); char* safe_strcat(char* destination, const char* source, size_t size); -char* safe_strcat_path(char* destination, const char* source, size_t size); #if defined(_WIN32) char* strcasestr(const char* haystack, const char* needle); #endif