diff --git a/src/music.cpp b/src/music.cpp index 558ddfb03d..c30e286894 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -12,6 +12,7 @@ #include "base_media_func.h" #include "base_media_music.h" #include "random_access_file_type.h" +#include "core/string_consumer.hpp" #include "safeguards.h" @@ -176,10 +177,13 @@ bool MusicSet::FillSetDetails(const IniFile &ini, const std::string &path, const item = trimmed_filename != nullptr && timingtrim != nullptr ? timingtrim->GetItem(trimmed_filename) : nullptr; if (item != nullptr && item->value.has_value() && !item->value->empty()) { - auto endpos = item->value->find(':'); - if (endpos != std::string::npos) { - this->songinfo[i].override_start = atoi(item->value->c_str()); - this->songinfo[i].override_end = atoi(item->value->c_str() + endpos + 1); + StringConsumer consumer{*item->value}; + auto start = consumer.TryReadIntegerBase(10); + auto valid = consumer.ReadIf(":"); + auto end = consumer.TryReadIntegerBase(10); + if (start.has_value() && valid && end.has_value() && !consumer.AnyBytesLeft()) { + this->songinfo[i].override_start = *start; + this->songinfo[i].override_end = *end; } } } diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 8728d931a2..e67fecfaec 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -22,6 +22,7 @@ #include "../strings_func.h" #include "../timer/timer.h" #include "../timer/timer_window.h" +#include "../core/string_consumer.hpp" #include "network_content.h" #include "table/strings.h" @@ -628,78 +629,66 @@ void ClientNetworkContentSocketHandler::OnReceiveData(std::unique_ptr da /* When we haven't opened a file this must be our first packet with metadata. */ this->cur_info = std::make_unique(); -/** Check p for not being null and return calling OnFailure if that's not the case. */ -#define check_not_null(p) { if ((p) == nullptr) { this->OnFailure(); return; } } -/** Check p for not being null and then terminate, or return calling OnFailure. */ -#define check_and_terminate(p) { check_not_null(p); *(p) = '\0'; } + try { + for (;;) { + std::string_view buffer{this->http_response.data(), this->http_response.size()}; + buffer.remove_prefix(this->http_response_index); + auto len = buffer.find('\n'); + if (len == std::string_view::npos) throw std::exception{}; + /* Update the index for the next one */ + this->http_response_index += static_cast(len + 1); - for (;;) { - char *str = this->http_response.data() + this->http_response_index; - char *p = strchr(str, '\n'); - check_and_terminate(p); + StringConsumer consumer{buffer.substr(0, len)}; - /* Update the index for the next one */ - this->http_response_index += (int)strlen(str) + 1; + /* Read the ID */ + this->cur_info->id = static_cast(consumer.ReadIntegerBase(10)); + if (!consumer.ReadIf(",")) throw std::exception{}; - /* Read the ID */ - p = strchr(str, ','); - check_and_terminate(p); - this->cur_info->id = (ContentID)atoi(str); + /* Read the type */ + this->cur_info->type = static_cast(consumer.ReadIntegerBase(10)); + if (!consumer.ReadIf(",")) throw std::exception{}; - /* Read the type */ - str = p + 1; - p = strchr(str, ','); - check_and_terminate(p); - this->cur_info->type = (ContentType)atoi(str); + /* Read the file size */ + this->cur_info->filesize = consumer.ReadIntegerBase(10); + if (!consumer.ReadIf(",")) throw std::exception{}; - /* Read the file size */ - str = p + 1; - p = strchr(str, ','); - check_and_terminate(p); - this->cur_info->filesize = atoi(str); + /* Read the URL */ + auto url = consumer.GetLeftData(); - /* Read the URL */ - str = p + 1; - /* Is it a fallback URL? If so, just continue with the next one. */ - if (strncmp(str, "ottd", 4) == 0) { - if ((uint)this->http_response_index >= this->http_response.size()) { + /* Is it a fallback URL? If so, just continue with the next one. */ + if (consumer.ReadIf("ottd")) { /* Have we gone through all lines? */ - this->OnFailure(); - return; + if (static_cast(this->http_response_index) >= this->http_response.size()) throw std::exception{}; + continue; } - continue; - } - p = strrchr(str, '/'); - check_not_null(p); - p++; // Start after the '/' + consumer.SkipUntilChar('/', StringConsumer::KEEP_SEPARATOR); + std::string_view filename; + /* Skip all but the last part. There must be at least one / though */ + do { + if (!consumer.ReadIf("/")) throw std::exception{}; + filename = consumer.ReadUntilChar('/', StringConsumer::KEEP_SEPARATOR); + } while (consumer.AnyBytesLeft()); - std::string filename = p; - /* Remove the extension from the string. */ - for (uint i = 0; i < 2; i++) { - auto pos = filename.find_last_of('.'); - if (pos == std::string::npos) { - this->OnFailure(); - return; + /* Remove the extension from the string. */ + for (uint i = 0; i < 2; i++) { + auto pos = filename.find_last_of('.'); + if (pos == std::string::npos) throw std::exception{}; + filename = filename.substr(0, pos); } - filename.erase(pos); + + /* Copy the string, without extension, to the filename. */ + this->cur_info->filename = filename; + + /* Request the next file. */ + if (!this->BeforeDownload()) throw std::exception{}; + + NetworkHTTPSocketHandler::Connect(url, this); + break; } - - /* Copy the string, without extension, to the filename. */ - this->cur_info->filename = std::move(filename); - - /* Request the next file. */ - if (!this->BeforeDownload()) { - this->OnFailure(); - return; - } - - NetworkHTTPSocketHandler::Connect(str, this); - return; + } catch (const std::exception&) { + this->OnFailure(); } - -#undef check -#undef check_and_terminate } /** Connect to the content server. */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 617c9cafc4..6c7f14462c 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -80,6 +80,7 @@ #include "timer/timer_game_realtime.h" #include "timer/timer_game_tick.h" #include "social_integration.h" +#include "core/string_consumer.hpp" #include "linkgraph/linkgraphschedule.h" @@ -272,14 +273,17 @@ static void WriteSavegameInfo(const std::string &name) */ static void ParseResolution(Dimension *res, const char *s) { - const char *t = strchr(s, 'x'); - if (t == nullptr) { + StringConsumer consumer(std::string_view{s}); + auto width = consumer.TryReadIntegerBase(10); + auto valid = consumer.ReadIf("x"); + auto height = consumer.TryReadIntegerBase(10); + if (!width.has_value() || !valid || !height.has_value() || consumer.AnyBytesLeft()) { ShowInfo("Invalid resolution '{}'", s); return; } - res->width = std::max(std::strtoul(s, nullptr, 0), 64UL); - res->height = std::max(std::strtoul(t + 1, nullptr, 0), 64UL); + res->width = std::max(*width, 64); + res->height = std::max(*height, 64); } diff --git a/src/settings.cpp b/src/settings.cpp index 44fc20be29..670a0d3c00 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -248,29 +248,19 @@ static std::optional> ParseIntList(const char *p) bool comma = false; // do we accept comma? std::vector result; - while (*p != '\0') { - switch (*p) { - case ',': - /* Do not accept multiple commas between numbers */ - if (!comma) return std::nullopt; - comma = false; - [[fallthrough]]; - - case ' ': - p++; - break; - - default: { - char *end; - unsigned long v = std::strtoul(p, &end, 0); - if (p == end) return std::nullopt; // invalid character (not a number) - - result.push_back(ClampTo(v)); - p = end; // first non-number - comma = true; // we accept comma now - break; - } + StringConsumer consumer{std::string_view{p}}; + for (;;) { + consumer.SkipUntilCharNotIn(StringConsumer::WHITESPACE_NO_NEWLINE); + if (!consumer.AnyBytesLeft()) break; + if (comma && consumer.ReadIf(",")) { + /* commas are optional, but we only accept one between values */ + comma = false; + continue; } + auto v = consumer.TryReadIntegerBase(10); + if (!v.has_value()) return std::nullopt; + result.push_back(*v); + comma = true; } /* If we have read comma but no number after it, fail. diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index 9bd4d0026d..d3dfe12da6 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -34,6 +34,7 @@ #include "../debug.h" #include "../blitter/factory.hpp" #include "../zoom_func.h" +#include "../core/string_consumer.hpp" #include "../table/opengl_shader.h" #include "../table/sprites.h" @@ -539,9 +540,13 @@ std::optional OpenGLBackend::Init(const Dimension &screen_res) if (strncmp(renderer, "llvmpipe", 8) == 0 || strncmp(renderer, "softpipe", 8) == 0) return "Software renderer detected, not using OpenGL"; #endif - const char *minor = strchr(ver, '.'); - _gl_major_ver = atoi(ver); - _gl_minor_ver = minor != nullptr ? atoi(minor + 1) : 0; + StringConsumer consumer{std::string_view{ver}}; + _gl_major_ver = consumer.ReadIntegerBase(10); + if (consumer.ReadIf(".")) { + _gl_minor_ver = consumer.ReadIntegerBase(10); + } else { + _gl_minor_ver = 0; + } #ifdef _WIN32 /* Old drivers on Windows (especially if made by Intel) seem to be