diff --git a/src/base_media_func.h b/src/base_media_func.h index ea19f10c4b..cdcf3c2200 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -14,6 +14,7 @@ #include "ini_type.h" #include "string_func.h" #include "error_func.h" +#include "core/string_consumer.hpp" extern void CheckExternalFiles(); @@ -70,7 +71,12 @@ bool BaseSet::FillSetDetails(const IniFile &ini, const std::string &path, con } fetch_metadata("version"); - this->version = atoi(item->value->c_str()); + auto value = ParseInteger(*item->value); + if (!value.has_value()) { + Debug(grf, 0, "Base {}set detail loading: {} field is invalid: {}", BaseSet::SET_TYPE, item->name, *item->value); + return false; + } + this->version = *value; item = metadata->GetItem("fallback"); this->fallback = (item != nullptr && item->value && *item->value != "0" && *item->value != "false"); diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index 7583da680b..0b2c502d8a 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -35,6 +35,7 @@ #include "timer/timer.h" #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" +#include "core/string_consumer.hpp" #include "widgets/cheat_widget.h" @@ -607,12 +608,13 @@ struct CheatWindow : Window { int32_t value; if (!str->empty()) { - long long llvalue = atoll(str->c_str()); + auto llvalue = ParseInteger(*str); + if (!llvalue.has_value()) return; /* Save the correct currency-translated value */ - if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue /= GetCurrency().rate; + if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue = *llvalue / GetCurrency().rate; - value = ClampTo(llvalue); + value = ClampTo(*llvalue); } else { value = sd->GetDefaultValue(); } @@ -621,11 +623,12 @@ struct CheatWindow : Window { } else { const CheatEntry *ce = &_cheats_ui[clicked_cheat]; int oldvalue = static_cast(ReadValue(ce->variable, ce->type)); - int value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; *ce->been_used = true; - value = ce->proc(value, value - oldvalue); + value = ce->proc(*value, *value - oldvalue); - if (value != oldvalue) WriteValue(ce->variable, ce->type, static_cast(value)); + if (*value != oldvalue) WriteValue(ce->variable, ce->type, static_cast(*value)); } this->valuewindow_entry = nullptr; diff --git a/src/driver.cpp b/src/driver.cpp index c790c9ebbb..4daa3ce1e1 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -17,6 +17,7 @@ #include "video/video_driver.hpp" #include "string_func.h" #include "fileio_func.h" +#include "core/string_consumer.hpp" #include "table/strings.h" @@ -77,7 +78,10 @@ bool GetDriverParamBool(const StringList &parm, const char *name) int GetDriverParamInt(const StringList &parm, const char *name, int def) { const char *p = GetDriverParam(parm, name); - return p != nullptr ? atoi(p) : def; + if (p == nullptr) return def; + auto value = ParseInteger(p); + if (value.has_value()) return *value; + UserError("Invalid value for driver parameter {}: {}", name, p); } /** diff --git a/src/game/game_gui.cpp b/src/game/game_gui.cpp index 0fe11b1eba..4a0b352a20 100644 --- a/src/game/game_gui.cpp +++ b/src/game/game_gui.cpp @@ -18,6 +18,7 @@ #include "../dropdown_func.h" #include "../timer/timer.h" #include "../timer/timer_window.h" +#include "../core/string_consumer.hpp" #include "game.hpp" #include "game_gui.hpp" @@ -346,9 +347,10 @@ struct GSConfigWindow : public Window { void OnQueryTextFinished(std::optional str) override { - if (!str.has_value() || str->empty()) return; - int32_t value = atoi(str->c_str()); - SetValue(value); + if (!str.has_value()) return; + auto value = ParseInteger(*str); + if (!value.has_value()) return; + SetValue(*value); } void OnDropdownSelect(WidgetID widget, int index) override diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 854ae3d226..7b911ff6d7 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -33,6 +33,7 @@ #include "ai/ai_gui.hpp" #include "game/game_gui.hpp" #include "industry.h" +#include "core/string_consumer.hpp" #include "widgets/genworld_widget.h" @@ -913,7 +914,9 @@ struct GenerateLandscapeWindow : public Window { int32_t value; if (!str->empty()) { - value = atoi(str->c_str()); + auto val = ParseInteger(*str); + if (!val.has_value()) return; + value = *val; } else { /* An empty string means revert to the default */ switch (this->widget_id) { @@ -1198,19 +1201,20 @@ struct CreateScenarioWindow : public Window void OnQueryTextFinished(std::optional str) override { - if (!str.has_value() || str->empty()) return; + if (!str.has_value()) return; - int32_t value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; switch (this->widget_id) { case WID_CS_START_DATE_TEXT: this->SetWidgetDirty(WID_CS_START_DATE_TEXT); - _settings_newgame.game_creation.starting_year = Clamp(TimerGameCalendar::Year(value), CalendarTime::MIN_YEAR, CalendarTime::MAX_YEAR); + _settings_newgame.game_creation.starting_year = Clamp(TimerGameCalendar::Year(*value), CalendarTime::MIN_YEAR, CalendarTime::MAX_YEAR); break; case WID_CS_FLAT_LAND_HEIGHT_TEXT: this->SetWidgetDirty(WID_CS_FLAT_LAND_HEIGHT_TEXT); - _settings_newgame.game_creation.se_flat_world_height = Clamp(value, 0, GetMapHeightLimit()); + _settings_newgame.game_creation.se_flat_world_height = Clamp(*value, 0, GetMapHeightLimit()); break; } diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 8ea8d2339a..d4bcdce7f9 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -48,6 +48,7 @@ #include "timer/timer.h" #include "timer/timer_window.h" #include "hotkeys.h" +#include "core/string_consumer.hpp" #include "widgets/industry_widget.h" @@ -1142,16 +1143,17 @@ public: if (!str.has_value() || str->empty()) return; Industry *i = Industry::Get(this->window_number); - uint value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; switch (this->editbox_line) { case IL_NONE: NOT_REACHED(); case IL_MULTIPLIER: - i->prod_level = ClampU(RoundDivSU(value * PRODLEVEL_DEFAULT, 100), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM); + i->prod_level = ClampU(RoundDivSU(*value * PRODLEVEL_DEFAULT, 100), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM); break; default: - i->produced[this->editbox_line - IL_RATE1].rate = ClampU(RoundDivSU(value, 8), 0, 255); + i->produced[this->editbox_line - IL_RATE1].rate = ClampU(RoundDivSU(*value, 8), 0, 255); break; } UpdateIndustryProduction(i); diff --git a/src/music.cpp b/src/music.cpp index c30e286894..586b17029e 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -134,7 +134,12 @@ bool MusicSet::FillSetDetails(const IniFile &ini, const std::string &path, const if (item != nullptr && item->value.has_value() && !item->value->empty()) { /* Song has a CAT file index, assume it's MPS MIDI format */ this->songinfo[i].filetype = MTT_MPSMIDI; - this->songinfo[i].cat_index = atoi(item->value->c_str()); + auto value = ParseInteger(*item->value); + if (!value.has_value()) { + Debug(grf, 0, "Invalid base music set song index: {}/{}", filename, *item->value); + continue; + } + this->songinfo[i].cat_index = *value; auto songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index); if (!songname.has_value()) { Debug(grf, 0, "Base music set song missing from CAT file: {}/{}", filename, this->songinfo[i].cat_index); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index b2ca4ab126..f24791d449 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -41,6 +41,7 @@ #include "../timer/timer_game_calendar.h" #include "../textfile_gui.h" #include "../stringfilter_type.h" +#include "../core/string_consumer.hpp" #include "../widgets/network_widget.h" @@ -1120,12 +1121,13 @@ struct NetworkStartServerWindow : public Window { if (this->widget_id == WID_NSS_SETPWD) { _settings_client.network.server_password = std::move(*str); } else { - int32_t value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; this->SetWidgetDirty(this->widget_id); switch (this->widget_id) { default: NOT_REACHED(); - case WID_NSS_CLIENTS_TXT: _settings_client.network.max_clients = Clamp(value, 2, MAX_CLIENTS); break; - case WID_NSS_COMPANIES_TXT: _settings_client.network.max_companies = Clamp(value, 1, MAX_COMPANIES); break; + case WID_NSS_CLIENTS_TXT: _settings_client.network.max_clients = Clamp(*value, 2, MAX_CLIENTS); break; + case WID_NSS_COMPANIES_TXT: _settings_client.network.max_companies = Clamp(*value, 1, MAX_COMPANIES); break; } } diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 1f83160969..c9df8d3cba 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -19,6 +19,7 @@ #include "textbuf_gui.h" #include "vehicle_gui.h" #include "zoom_func.h" +#include "core/string_consumer.hpp" #include "engine_base.h" #include "industry.h" @@ -1078,9 +1079,11 @@ struct SpriteAlignerWindow : Window { void OnQueryTextFinished(std::optional str) override { - if (!str.has_value() || str->empty()) return; + if (!str.has_value()) return; - this->current_sprite = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; + this->current_sprite = *value; if (this->current_sprite >= GetMaxSpriteID()) this->current_sprite = 0; while (GetSpriteType(this->current_sprite) != SpriteType::Normal) { this->current_sprite = (this->current_sprite + 1) % GetMaxSpriteID(); diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 7cbc3d064a..2005d6de38 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -31,6 +31,7 @@ #include "timer/timer.h" #include "timer/timer_window.h" #include "zoom_func.h" +#include "core/string_consumer.hpp" #include "widgets/newgrf_widget.h" #include "widgets/misc_widget.h" @@ -438,9 +439,10 @@ struct NewGRFParametersWindow : public Window { void OnQueryTextFinished(std::optional str) override { if (!str.has_value() || str->empty()) return; - int32_t value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; GRFParameterInfo &par_info = this->GetParameterInfo(this->clicked_row); - this->grf_config.SetValue(par_info, value); + this->grf_config.SetValue(par_info, *value); this->SetDirty(); } diff --git a/src/openttd.cpp b/src/openttd.cpp index 6c7f14462c..1a6bcce33d 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -553,7 +553,13 @@ int openttd_main(std::span arguments) scanner->join_server_password = mgo.opt; break; case 'r': ParseResolution(&resolution, mgo.opt); break; - case 't': scanner->startyear = TimerGameCalendar::Year(atoi(mgo.opt)); break; + case 't': + if (auto value = ParseInteger(mgo.opt); value.has_value()) { + scanner->startyear = TimerGameCalendar::Year(*value); + } else { + fmt::print(stderr, "Invalid start year: {}\n", mgo.opt); + } + break; case 'd': { #if defined(_WIN32) CreateConsole(); diff --git a/src/order_gui.cpp b/src/order_gui.cpp index a3a285ca35..80bb8474ea 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -34,6 +34,7 @@ #include "error.h" #include "order_cmd.h" #include "company_cmd.h" +#include "core/string_consumer.hpp" #include "widgets/order_widget.h" @@ -1362,22 +1363,23 @@ public: if (!str.has_value() || str->empty()) return; VehicleOrderID sel = this->OrderGetSel(); - uint value = atoi(str->c_str()); + auto value = ParseInteger(*str); + if (!value.has_value()) return; switch (this->vehicle->GetOrder(sel)->GetConditionVariable()) { case OCV_MAX_SPEED: - value = ConvertDisplaySpeedToSpeed(value, this->vehicle->type); + value = ConvertDisplaySpeedToSpeed(*value, this->vehicle->type); break; case OCV_RELIABILITY: case OCV_LOAD_PERCENTAGE: - value = Clamp(value, 0, 100); + value = Clamp(*value, 0, 100); break; default: break; } - Command::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index, sel, MOF_COND_VALUE, Clamp(value, 0, 2047)); + Command::Post(STR_ERROR_CAN_T_MODIFY_THIS_ORDER, this->vehicle->tile, this->vehicle->index, sel, MOF_COND_VALUE, Clamp(*value, 0, 2047)); } void OnDropdownSelect(WidgetID widget, int index) override diff --git a/src/safeguards.h b/src/safeguards.h index 1ab12a65e9..e5919c3de3 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -51,6 +51,11 @@ #define sscanf SAFEGUARD_DO_NOT_USE_THIS_METHOD #define from_string SAFEGUARD_DO_NOT_USE_THIS_METHOD +/* Use ParseInteger or StringConsumer instead. */ +#define atoi SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define atol SAFEGUARD_DO_NOT_USE_THIS_METHOD +#define atoll SAFEGUARD_DO_NOT_USE_THIS_METHOD + /* Use fmt::print instead. */ #define printf SAFEGUARD_DO_NOT_USE_THIS_METHOD #define fprintf SAFEGUARD_DO_NOT_USE_THIS_METHOD diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index c2f4a02a74..601cc43eff 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -25,6 +25,7 @@ #include "../strings_func.h" #include "../timer/timer.h" #include "../timer/timer_window.h" +#include "../core/string_consumer.hpp" #include "script_gui.h" #include "script_log.hpp" @@ -495,10 +496,10 @@ struct ScriptSettingsWindow : public Window { void OnQueryTextFinished(std::optional str) override { - if (!str.has_value() || str->empty()) return; - int32_t value = atoi(str->c_str()); - - SetValue(value); + if (!str.has_value()) return; + auto value = ParseInteger(*str); + if (!value.has_value()) return; + SetValue(*value); } void OnDropdownSelect(WidgetID widget, int index) override diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp index 57a1b92a9c..1fcdf3767e 100644 --- a/src/script/script_info.cpp +++ b/src/script/script_info.cpp @@ -14,6 +14,7 @@ #include "script_info.hpp" #include "script_scanner.hpp" +#include "../core/string_consumer.hpp" #include "../3rdparty/fmt/format.h" #include "../safeguards.h" @@ -229,8 +230,9 @@ SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm) sign = -1; key_string++; } - int key = atoi(key_string) * sign; - config->labels[key] = StrMakeValid(label); + auto key = ParseInteger(key_string); + if (!key.has_value()) return SQ_ERROR; + config->labels[*key * sign] = StrMakeValid(label); sq_pop(vm, 2); } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 8004abaa75..f0c6861a82 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -53,6 +53,7 @@ #include "social_integration.h" #include "sound_func.h" #include "settingentry_gui.h" +#include "core/string_consumer.hpp" #include "table/strings.h" @@ -1371,12 +1372,13 @@ struct GameOptionsWindow : Window { int32_t value; if (!str->empty()) { - long long llvalue = atoll(str->c_str()); + auto llvalue = ParseInteger(*str); + if (!llvalue.has_value()) return; /* Save the correct currency-translated value */ - if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue /= GetCurrency().rate; + if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue = *llvalue / GetCurrency().rate; - value = ClampTo(llvalue); + value = ClampTo(*llvalue); } else { value = sd->GetDefaultValue(); } @@ -2065,9 +2067,12 @@ struct CustomCurrencyWindow : Window { if (!str.has_value()) return; switch (this->query_widget) { - case WID_CC_RATE: - GetCustomCurrency().rate = Clamp(atoi(str->c_str()), 1, UINT16_MAX); + case WID_CC_RATE: { + auto val = ParseInteger(*str); + if (!val.has_value()) return; + GetCustomCurrency().rate = Clamp(*val, 1, UINT16_MAX); break; + } case WID_CC_SEPARATOR: // Thousands separator GetCustomCurrency().separator = std::move(*str); @@ -2082,9 +2087,13 @@ struct CustomCurrencyWindow : Window { break; case WID_CC_YEAR: { // Year to switch to euro - TimerGameCalendar::Year val{atoi(str->c_str())}; - - GetCustomCurrency().to_euro = (val < MIN_EURO_YEAR ? CF_NOEURO : std::min(val, CalendarTime::MAX_YEAR)); + TimerGameCalendar::Year year = CF_NOEURO; + if (!str->empty()) { + auto val = ParseInteger(*str); + if (!val.has_value()) return; + year = Clamp(static_cast(*val), MIN_EURO_YEAR, CalendarTime::MAX_YEAR); + } + GetCustomCurrency().to_euro = year; break; } } diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index a1541b77e9..16a1949c12 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -59,6 +59,7 @@ #include "timer/timer_window.h" #include "timer/timer_game_calendar.h" #include "help_gui.h" +#include "core/string_consumer.hpp" #include "widgets/toolbar_widget.h" @@ -2500,7 +2501,9 @@ struct ScenarioEditorToolbarWindow : Window { TimerGameCalendar::Year value; if (!str->empty()) { - value = TimerGameCalendar::Year{atoi(str->c_str())}; + auto val = ParseInteger(*str); + if (!val.has_value()) return; + value = static_cast(*val); } else { /* An empty string means revert to the default */ value = TimerGameCalendar::Year{CalendarTime::DEF_START_YEAR.base()};