From 28b0a39a73ce25903cedec561df163a572e6077a Mon Sep 17 00:00:00 2001 From: Silent Date: Wed, 16 Mar 2022 20:42:20 +0100 Subject: [PATCH 1/2] Refactor CmdlineSprite.cpp and remove path_end_with_separator --- src/openrct2/CmdlineSprite.cpp | 134 +++++++++++---------------------- src/openrct2/util/Util.cpp | 12 --- src/openrct2/util/Util.h | 2 - 3 files changed, 43 insertions(+), 105 deletions(-) diff --git a/src/openrct2/CmdlineSprite.cpp b/src/openrct2/CmdlineSprite.cpp index 2258c90bd3..e06ed64dae 100644 --- a/src/openrct2/CmdlineSprite.cpp +++ b/src/openrct2/CmdlineSprite.cpp @@ -27,9 +27,9 @@ #include #include -#ifdef _WIN32 -# include "core/String.hpp" -#endif +// TODO: Remove when C++20 is enabled and std::format can be used +#include +#include using namespace OpenRCT2::Drawing; @@ -183,12 +183,11 @@ bool SpriteFile::Save(const utf8* path) } } -static bool SpriteImageExport(const rct_g1_element& spriteElement, const char* outPath) +static bool SpriteImageExport(const rct_g1_element& spriteElement, u8string_view outPath) { - const auto pixelBufferSize = spriteElement.width * spriteElement.height; + const size_t pixelBufferSize = static_cast(spriteElement.width) * spriteElement.height; auto pixelBuffer = std::make_unique(pixelBufferSize); auto pixels = pixelBuffer.get(); - std::fill_n(pixels, pixelBufferSize, 0x00); rct_drawpixelinfo dpi; dpi.bits = pixels; @@ -255,6 +254,15 @@ static std::optional SpriteImageImport( } } +// TODO: Remove when C++20 is enabled and std::format can be used +static std::string PopStr(std::ostringstream& oss) +{ + auto str = oss.str(); + oss.str(""); + oss.clear(); + return str; +} + int32_t cmdline_for_sprite(const char** argv, int32_t argc) { gOpenRCT2Headless = true; @@ -271,7 +279,7 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) if (argc == 2) { - const char* spriteFilePath = argv[1]; + const utf8* spriteFilePath = argv[1]; auto spriteFile = SpriteFile::Open(spriteFilePath); if (!spriteFile.has_value()) { @@ -284,7 +292,7 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return 1; } - const char* spriteFilePath = argv[1]; + const utf8* spriteFilePath = argv[1]; int32_t spriteIndex = atoi(argv[2]); auto spriteFile = SpriteFile::Open(spriteFilePath); if (!spriteFile.has_value()) @@ -316,9 +324,9 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - const char* spriteFilePath = argv[1]; + const utf8* spriteFilePath = argv[1]; int32_t spriteIndex = atoi(argv[2]); - const char* outputPath = argv[3]; + const utf8* outputPath = argv[3]; auto spriteFile = SpriteFile::Open(spriteFilePath); if (!spriteFile.has_value()) { @@ -349,8 +357,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - const char* spriteFilePath = argv[1]; - char outputPath[MAX_PATH]; + const utf8* spriteFilePath = argv[1]; + const utf8* outputPath = argv[2]; auto spriteFile = SpriteFile::Open(spriteFilePath); if (!spriteFile.has_value()) @@ -359,55 +367,29 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - safe_strcpy(outputPath, argv[2], MAX_PATH); - path_end_with_separator(outputPath, MAX_PATH); - if (!Platform::EnsureDirectoryExists(outputPath)) { fprintf(stderr, "Unable to create directory.\n"); return -1; } - int32_t maxIndex = static_cast(spriteFile->Header.num_entries); - int32_t numbers = static_cast(std::floor(std::log(maxIndex))); - size_t pathLen = strlen(outputPath); + const uint32_t maxIndex = spriteFile->Header.num_entries; + const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); - if (pathLen >= static_cast(MAX_PATH - numbers - 5)) + std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used + for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) { - fprintf(stderr, "Path too long.\n"); - return -1; - } + // Status indicator + printf("\r%u / %u, %u%%", spriteIndex + 1, maxIndex, ((spriteIndex + 1) * 100) / maxIndex); - for (int32_t x = 0; x < numbers; x++) - { - outputPath[pathLen + x] = '0'; - } - safe_strcpy(outputPath + pathLen + numbers, ".png", MAX_PATH - pathLen - numbers); - - for (int32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) - { - if (spriteIndex % 100 == 99) - { - // Status indicator - printf("\r%d / %d, %d%%", spriteIndex, maxIndex, spriteIndex / maxIndex); - } + oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; const auto& spriteHeader = spriteFile->Entries[spriteIndex]; - if (!SpriteImageExport(spriteHeader, outputPath)) + if (!SpriteImageExport(spriteHeader, Path::Combine(outputPath, PopStr(oss)))) { fprintf(stderr, "Could not export\n"); return -1; } - - // Add to the index at the end of the file name - char* counter = outputPath + pathLen + numbers - 1; - (*counter)++; - while (*counter > '9') - { - *counter = '0'; - counter--; - (*counter)++; - } } return 1; } @@ -420,14 +402,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - char datName[DAT_NAME_LENGTH + 1] = { 0 }; - std::fill_n(datName, DAT_NAME_LENGTH, ' '); - int32_t i = 0; - for (const char* ch = argv[1]; *ch != '\0' && i < DAT_NAME_LENGTH; ch++) - { - datName[i++] = *ch; - } - + const char* datName = argv[1]; + const utf8* outputPath = argv[2]; auto context = OpenRCT2::CreateContext(); context->Initialise(); @@ -451,53 +427,29 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) auto& objManager = context->GetObjectManager(); const auto* const metaObject = objManager.GetLoadedObject(objectType, entryIndex); - char outputPath[MAX_PATH]; - safe_strcpy(outputPath, argv[2], MAX_PATH); - path_end_with_separator(outputPath, MAX_PATH); - if (!Platform::EnsureDirectoryExists(outputPath)) { fprintf(stderr, "Unable to create directory.\n"); return -1; } - auto maxIndex = metaObject->GetNumImages(); - - int32_t numDigits = std::max(1, static_cast(std::floor(std::log(maxIndex)))); - size_t pathLen = strlen(outputPath); - - if (pathLen >= static_cast(MAX_PATH - numDigits - 5)) - { - fprintf(stderr, "Path too long.\n"); - return -1; - } - - for (int32_t x = 0; x < numDigits; x++) - { - outputPath[pathLen + x] = '0'; - } - safe_strcpy(outputPath + pathLen + numDigits, ".png", MAX_PATH - pathLen - numDigits); + const uint32_t maxIndex = metaObject->GetNumImages(); + const int32_t numbers = static_cast(std::floor(std::log10(maxIndex) + 1)); + std::ostringstream oss; // TODO: Remove when C++20 is enabled and std::format can be used for (uint32_t spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++) { + oss << std::setw(numbers) << std::setfill('0') << spriteIndex << ".png"; + auto path = Path::Combine(outputPath, PopStr(oss)); + const auto& g1 = metaObject->GetImageTable().GetImages()[spriteIndex]; - if (!SpriteImageExport(g1, outputPath)) + if (!SpriteImageExport(g1, path)) { fprintf(stderr, "Could not export\n"); return -1; } - fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d },\n", outputPath, g1.x_offset, g1.y_offset); - - // Add to the index at the end of the file name - char* counter = outputPath + pathLen + numDigits - 1; - (*counter)++; - while (*counter > '9') - { - *counter = '0'; - counter--; - (*counter)++; - } + fprintf(stdout, "{ \"path\": \"%s\", \"x\": %d, \"y\": %d },\n", path.c_str(), g1.x_offset, g1.y_offset); } return 1; } @@ -510,7 +462,7 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - const char* spriteFilePath = argv[1]; + const utf8* spriteFilePath = argv[1]; SpriteFile spriteFile; spriteFile.Save(spriteFilePath); @@ -525,8 +477,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - const char* spriteFilePath = argv[1]; - const char* imagePath = argv[2]; + const utf8* spriteFilePath = argv[1]; + const utf8* imagePath = argv[2]; int16_t x_offset = 0; int16_t y_offset = 0; @@ -577,8 +529,8 @@ int32_t cmdline_for_sprite(const char** argv, int32_t argc) return -1; } - const char* spriteFilePath = argv[1]; - const char* spriteDescriptionPath = argv[2]; + const utf8* spriteFilePath = argv[1]; + const utf8* spriteDescriptionPath = argv[2]; const auto directoryPath = Path::GetDirectory(spriteDescriptionPath); json_t jsonSprites = Json::ReadFromFile(spriteDescriptionPath); diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index 9adad3b3dd..5a5574a540 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -62,18 +62,6 @@ bool filename_valid_characters(const utf8* filename) return true; } -void path_end_with_separator(utf8* path, size_t size) -{ - size_t length = strnlen(path, size); - if (length >= size - 1) - return; - - if ((length == 0) || ((path[length - 1] != *PATH_SEPARATOR) && path[length - 1] != '/')) - { - safe_strcat(path, PATH_SEPARATOR, size); - } -} - int32_t bitscanforward(int32_t source) { #if defined(_MSC_VER) && (_MSC_VER >= 1400) // Visual Studio 2005 diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index ee1b19745b..b0f82f6fad 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -25,8 +25,6 @@ int32_t mph_to_dmps(int32_t mph); bool filename_valid_characters(const utf8* filename); -void path_end_with_separator(utf8* path, size_t size); - bool sse41_available(); bool avx2_available(); From 771637f24c5ec1e38d0afc7feb84aa5c0870f5d7 Mon Sep 17 00:00:00 2001 From: Silent Date: Wed, 16 Mar 2022 21:02:42 +0100 Subject: [PATCH 2/2] Refactor filename_valid_characters into Platform::IsFilenameValid --- src/openrct2-ui/windows/LoadSave.cpp | 4 ++-- src/openrct2-ui/windows/Themes.cpp | 4 ++-- src/openrct2-ui/windows/TitleEditor.cpp | 6 +++--- src/openrct2-ui/windows/TrackDesignManage.cpp | 4 ++-- src/openrct2/platform/Platform.h | 1 + src/openrct2/platform/Shared.cpp | 18 ++++++++++++------ src/openrct2/util/Util.cpp | 11 ----------- src/openrct2/util/Util.h | 2 -- 8 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/openrct2-ui/windows/LoadSave.cpp b/src/openrct2-ui/windows/LoadSave.cpp index a7d07e6335..7c55c87547 100644 --- a/src/openrct2-ui/windows/LoadSave.cpp +++ b/src/openrct2-ui/windows/LoadSave.cpp @@ -579,7 +579,7 @@ static void WindowLoadsaveTextinput(rct_window* w, rct_widgetindex widgetIndex, { case WIDX_NEW_FOLDER: { - if (!filename_valid_characters(text)) + if (!Platform::IsFilenameValid(text)) { context_show_error(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); return; @@ -971,7 +971,7 @@ static bool IsValidPath(const char* path) // which handles multiple patterns auto filename = Path::GetFileNameWithoutExtension(path); - return filename_valid_characters(filename.c_str()); + return Platform::IsFilenameValid(filename); } static void WindowLoadsaveSelect(rct_window* w, const char* path) diff --git a/src/openrct2-ui/windows/Themes.cpp b/src/openrct2-ui/windows/Themes.cpp index 5af77f7cbd..607e1ebc40 100644 --- a/src/openrct2-ui/windows/Themes.cpp +++ b/src/openrct2-ui/windows/Themes.cpp @@ -19,8 +19,8 @@ #include #include #include +#include #include -#include enum { @@ -698,7 +698,7 @@ static void WindowThemesTextinput(rct_window* w, rct_widgetindex widgetIndex, ch { case WIDX_THEMES_DUPLICATE_BUTTON: case WIDX_THEMES_RENAME_BUTTON: - if (filename_valid_characters(text)) + if (Platform::IsFilenameValid(text)) { if (ThemeGetIndexForName(text) == SIZE_MAX) { diff --git a/src/openrct2-ui/windows/TitleEditor.cpp b/src/openrct2-ui/windows/TitleEditor.cpp index 53a76b5aa0..725b3dd2a9 100644 --- a/src/openrct2-ui/windows/TitleEditor.cpp +++ b/src/openrct2-ui/windows/TitleEditor.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include #include using namespace OpenRCT2; @@ -635,7 +635,7 @@ static void WindowTitleEditorTextinput(rct_window* w, rct_widgetindex widgetInde case WIDX_TITLE_EDITOR_NEW_BUTTON: case WIDX_TITLE_EDITOR_DUPLICATE_BUTTON: case WIDX_TITLE_EDITOR_RENAME_BUTTON: - if (filename_valid_characters(text)) + if (Platform::IsFilenameValid(text)) { if (title_sequence_manager_get_index_for_name(text) == SIZE_MAX) { @@ -1137,7 +1137,7 @@ static void WindowTitleEditorAddParkCallback(int32_t result, const utf8* path) static void WindowTitleEditorRenamePark(size_t index, const utf8* name) { - if (!filename_valid_characters(name)) + if (!Platform::IsFilenameValid(name)) { context_show_error(STR_ERROR_INVALID_CHARACTERS, STR_NONE, {}); return; diff --git a/src/openrct2-ui/windows/TrackDesignManage.cpp b/src/openrct2-ui/windows/TrackDesignManage.cpp index 1e0890085b..dcebe49508 100644 --- a/src/openrct2-ui/windows/TrackDesignManage.cpp +++ b/src/openrct2-ui/windows/TrackDesignManage.cpp @@ -14,8 +14,8 @@ #include #include #include +#include #include -#include static constexpr const rct_string_id WINDOW_TITLE = STR_STRING; static constexpr const int32_t WH = 44; @@ -164,7 +164,7 @@ static void WindowTrackManageTextinput(rct_window* w, rct_widgetindex widgetInde return; } - if (!filename_valid_characters(text)) + if (!Platform::IsFilenameValid(text)) { context_show_error(STR_CANT_RENAME_TRACK_DESIGN, STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS, {}); return; diff --git a/src/openrct2/platform/Platform.h b/src/openrct2/platform/Platform.h index 5aa9a04351..0a827fcd7d 100644 --- a/src/openrct2/platform/Platform.h +++ b/src/openrct2/platform/Platform.h @@ -71,6 +71,7 @@ namespace Platform uint64_t GetFileSize(std::string_view path); std::string ResolveCasing(std::string_view path, bool fileExists); std::string SanitiseFilename(std::string_view originalName); + bool IsFilenameValid(u8string_view fileName); uint16_t GetLocaleLanguage(); CurrencyType GetLocaleCurrency(); diff --git a/src/openrct2/platform/Shared.cpp b/src/openrct2/platform/Shared.cpp index 3bb9a2d216..e64f0ca90d 100644 --- a/src/openrct2/platform/Shared.cpp +++ b/src/openrct2/platform/Shared.cpp @@ -26,6 +26,12 @@ #include #include +#ifdef _WIN32 +static constexpr std::array _prohibitedCharacters = { '<', '>', '*', '\\', ':', '|', '?', '"', '/' }; +#else +static constexpr std::array _prohibitedCharacters = { '/' }; +#endif + namespace Platform { void CoreInit() @@ -96,22 +102,22 @@ namespace Platform std::string SanitiseFilename(std::string_view originalName) { -#ifdef _WIN32 - static constexpr std::array prohibited = { '<', '>', '*', '\\', ':', '|', '?', '"', '/' }; -#else - static constexpr std::array prohibited = { '/' }; -#endif auto sanitised = std::string(originalName); std::replace_if( sanitised.begin(), sanitised.end(), [](const std::string::value_type& ch) -> bool { - return std::find(prohibited.begin(), prohibited.end(), ch) != prohibited.end(); + return std::find(_prohibitedCharacters.begin(), _prohibitedCharacters.end(), ch) != _prohibitedCharacters.end(); }, '_'); sanitised = String::Trim(sanitised); return sanitised; } + bool IsFilenameValid(u8string_view fileName) + { + return fileName.find_first_of(_prohibitedCharacters.data(), 0, _prohibitedCharacters.size()) == fileName.npos; + } + #ifndef __ANDROID__ float GetDefaultScale() { diff --git a/src/openrct2/util/Util.cpp b/src/openrct2/util/Util.cpp index 5a5574a540..e4b9b7c6b7 100644 --- a/src/openrct2/util/Util.cpp +++ b/src/openrct2/util/Util.cpp @@ -51,17 +51,6 @@ int32_t mph_to_dmps(int32_t mph) return (mph * 73243) >> 14; } -bool filename_valid_characters(const utf8* filename) -{ - for (int32_t i = 0; filename[i] != '\0'; i++) - { - if (filename[i] == '\\' || filename[i] == '/' || filename[i] == ':' || filename[i] == '?' || filename[i] == '*' - || filename[i] == '<' || filename[i] == '>' || filename[i] == '|') - return false; - } - return true; -} - int32_t bitscanforward(int32_t source) { #if defined(_MSC_VER) && (_MSC_VER >= 1400) // Visual Studio 2005 diff --git a/src/openrct2/util/Util.h b/src/openrct2/util/Util.h index b0f82f6fad..01290233b4 100644 --- a/src/openrct2/util/Util.h +++ b/src/openrct2/util/Util.h @@ -23,8 +23,6 @@ int32_t metres_to_feet(int32_t metres); int32_t mph_to_kmph(int32_t mph); int32_t mph_to_dmps(int32_t mph); -bool filename_valid_characters(const utf8* filename); - bool sse41_available(); bool avx2_available();