From b0486a940d5f6247b8a38039e8ebb8775f432a89 Mon Sep 17 00:00:00 2001 From: rubidium Date: Thu, 4 Jul 2013 21:20:05 +0000 Subject: [PATCH] (svn r25560) [1.3] -Backport from trunk: - Fix: [Squirrel] Infinite recursion loop in freeing data via a looping set of references [FS#5568] (r25558) - Fix: One could build bridges over owned land of another company [FS#5524] (r25557) - Fix: [Script] Texts from scripts were not validated before they were shown, causing an assertion to trigger [FS#5632] (r25555) - Fix: Provide a warning when no vehicles are available, and tell what to do in that case [FS#5530] (r25553) --- src/3rdparty/squirrel/squirrel/squtils.h | 6 +++++- src/engine.cpp | 23 +++++++++++++++++++++++ src/engine_func.h | 1 + src/lang/english.txt | 3 +++ src/openttd.cpp | 1 + src/script/api/script_text.cpp | 2 ++ src/script/script_info.cpp | 6 ++++++ src/script/squirrel.cpp | 2 ++ src/string.cpp | 12 ++++++++++++ src/string_func.h | 2 ++ src/table/object_land.h | 2 +- 11 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/3rdparty/squirrel/squirrel/squtils.h b/src/3rdparty/squirrel/squirrel/squtils.h index 5f7e2e2393..111a1c383a 100644 --- a/src/3rdparty/squirrel/squirrel/squtils.h +++ b/src/3rdparty/squirrel/squirrel/squtils.h @@ -37,9 +37,13 @@ public: ~sqvector() { if(_allocated) { + /* Break freeing loops, if this vector (indirectly) links to itself. */ + size_t allocated_size = _allocated * sizeof(T); + _allocated = 0; + for(SQUnsignedInteger i = 0; i < _size; i++) _vals[i].~T(); - SQ_FREE(_vals, (_allocated * sizeof(T))); + SQ_FREE(_vals, allocated_size); } } void reserve(SQUnsignedInteger newsize) { _realloc(newsize); } diff --git a/src/engine.cpp b/src/engine.cpp index fa9b1dcff6..b09332c0fc 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -30,6 +30,7 @@ #include "company_base.h" #include "vehicle_func.h" #include "articulated_vehicles.h" +#include "error.h" #include "table/strings.h" #include "table/engines.h" @@ -1098,3 +1099,25 @@ bool IsEngineRefittable(EngineID engine) CargoID default_cargo = e->GetDefaultCargoType(); return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo; } + +/** + * Check for engines that have an appropriate availability. + */ +void CheckEngines() +{ + const Engine *e; + Date min_date = INT32_MAX; + + FOR_ALL_ENGINES(e) { + if (!e->IsEnabled()) continue; + + /* We have an available engine... yay! */ + if (e->flags & ENGINE_AVAILABLE && e->company_avail != 0) return; + + /* Okay, try to find the earliest date. */ + min_date = min(min_date, e->info.base_intro); + } + + SetDParam(0, min_date); + ShowErrorMessage(STR_ERROR_NO_VEHICLES_AVAILABLE, STR_ERROR_NO_VEHICLES_AVAILABLE_EXPLANATION, WL_WARNING); +} diff --git a/src/engine_func.h b/src/engine_func.h index bb7e2fcac6..faa8e8e024 100644 --- a/src/engine_func.h +++ b/src/engine_func.h @@ -18,6 +18,7 @@ void SetupEngines(); void StartupEngines(); +void CheckEngines(); /* Original engine data counts and offsets */ extern const uint8 _engine_counts[4]; diff --git a/src/lang/english.txt b/src/lang/english.txt index b498866f16..f8f09cde2e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4190,6 +4190,9 @@ STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Can't ch STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... vehicle is destroyed +STR_ERROR_NO_VEHICLES_AVAILABLE :{WHITE}No vehicles are available yet +STR_ERROR_NO_VEHICLES_AVAILABLE_EXPLANATION :{WHITE}Start a new game after {DATE_SHORT} or use a NewGRF that provides early vehicles + # Specific vehicle errors STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Can't make train pass signal at danger... STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}Can't reverse direction of train... diff --git a/src/openttd.cpp b/src/openttd.cpp index ffecade0a0..f81086c27d 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -916,6 +916,7 @@ static void MakeNewGameDone() if (_settings_client.gui.pause_on_newgame) DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE); + CheckEngines(); MarkWholeScreenDirty(); } diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index 577dd813e8..f87b2e8b28 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -72,6 +72,7 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) sq_getstring(vm, -1, &value); this->params[parameter] = strdup(SQ2OTTD(value)); + ValidateString(this->params[parameter]); break; } @@ -147,6 +148,7 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm) const SQChar *key; sq_getstring(vm, 2, &key); const char *key_string = SQ2OTTD(key); + ValidateString(key_string); if (strncmp(key_string, "param_", 6) != 0 || strlen(key_string) > 8) return SQ_ERROR; k = atoi(key_string + 6); diff --git a/src/script/script_info.cpp b/src/script/script_info.cpp index a1db9e13e0..8b84966f23 100644 --- a/src/script/script_info.cpp +++ b/src/script/script_info.cpp @@ -126,12 +126,15 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm) const SQChar *sqkey; if (SQ_FAILED(sq_getstring(vm, -2, &sqkey))) return SQ_ERROR; const char *key = SQ2OTTD(sqkey); + ValidateString(key); if (strcmp(key, "name") == 0) { const SQChar *sqvalue; if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR; char *name = strdup(SQ2OTTD(sqvalue)); char *s; + ValidateString(name); + /* Don't allow '=' and ',' in configure setting names, as we need those * 2 chars to nicely store the settings as a string. */ while ((s = strchr(name, '=')) != NULL) *s = '_'; @@ -142,6 +145,7 @@ SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm) const SQChar *sqdescription; if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR; config.description = strdup(SQ2OTTD(sqdescription)); + ValidateString(config.description); items |= 0x002; } else if (strcmp(key, "min_value") == 0) { SQInteger res; @@ -227,6 +231,7 @@ SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm) const SQChar *sq_setting_name; if (SQ_FAILED(sq_getstring(vm, -2, &sq_setting_name))) return SQ_ERROR; const char *setting_name = SQ2OTTD(sq_setting_name); + ValidateString(setting_name); ScriptConfigItem *config = NULL; for (ScriptConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) { @@ -255,6 +260,7 @@ SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm) const char *key_string = SQ2OTTD(sq_key); int key = atoi(key_string + 1); const char *label = SQ2OTTD(sq_label); + ValidateString(label); /* !Contains() prevents strdup from leaking. */ if (!config->labels->Contains(key)) config->labels->Insert(key, strdup(label)); diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index ed87d487cf..7c684d4b82 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -14,6 +14,7 @@ #include "../debug.h" #include "squirrel_std.hpp" #include "../fileio_func.h" +#include "../string_func.h" #include #include <../squirrel/sqpcheader.h> #include <../squirrel/sqvm.h> @@ -252,6 +253,7 @@ bool Squirrel::CallStringMethodStrdup(HSQOBJECT instance, const char *method_nam if (!this->CallMethod(instance, method_name, &ret, suspend)) return false; if (ret._type != OT_STRING) return false; *res = strdup(ObjectToString(&ret)); + ValidateString(*res); return true; } diff --git a/src/string.cpp b/src/string.cpp index b543ab868e..39fc8479c9 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -254,6 +254,18 @@ void str_validate(char *str, const char *last, StringValidationSettings settings *dst = '\0'; } +/** + * Scans the string for valid characters and if it finds invalid ones, + * replaces them with a question mark '?'. + * @param str the string to validate + */ +void ValidateString(const char *str) +{ + /* We know it is '\0' terminated. */ + str_validate(const_cast(str), str + strlen(str) + 1); +} + + /** * Checks whether the given string is valid, i.e. contains only * valid (printable) characters and is properly terminated. diff --git a/src/string_func.h b/src/string_func.h index 32d3d7e6a9..25608889dd 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -40,6 +40,8 @@ int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FO char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2); void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); +void ValidateString(const char *str); + void str_fix_scc_encoded(char *str, const char *last); void str_strip_colours(char *str); bool strtolower(char *str); diff --git a/src/table/object_land.h b/src/table/object_land.h index 3c7ac1bc02..ed15a11bfe 100644 --- a/src/table/object_land.h +++ b/src/table/object_land.h @@ -130,7 +130,7 @@ extern const ObjectSpec _original_objects[] = { M(STR_LAI_OBJECT_DESCRIPTION_TRANSMITTER, 0x11, 0, 0, 10, OBJECT_FLAG_CANNOT_REMOVE | OBJECT_FLAG_ONLY_IN_SCENEDIT), M(STR_LAI_OBJECT_DESCRIPTION_LIGHTHOUSE, 0x11, 0, 0, 8, OBJECT_FLAG_CANNOT_REMOVE | OBJECT_FLAG_ONLY_IN_SCENEDIT), M(STR_TOWN_BUILDING_NAME_STATUE_1, 0x11, 0, 0, 5, OBJECT_FLAG_CANNOT_REMOVE | OBJECT_FLAG_ONLY_IN_GAME | OBJECT_FLAG_ONLY_IN_SCENEDIT), // Yes, we disallow building this everywhere. Happens in "special" case! - M(STR_LAI_OBJECT_DESCRIPTION_COMPANY_OWNED_LAND, 0x11, 10, 10, 0, OBJECT_FLAG_AUTOREMOVE | OBJECT_FLAG_ONLY_IN_GAME | OBJECT_FLAG_CLEAR_INCOME | OBJECT_FLAG_HAS_NO_FOUNDATION | OBJECT_FLAG_ALLOW_UNDER_BRIDGE), + M(STR_LAI_OBJECT_DESCRIPTION_COMPANY_OWNED_LAND, 0x11, 10, 10, 0, OBJECT_FLAG_AUTOREMOVE | OBJECT_FLAG_ONLY_IN_GAME | OBJECT_FLAG_CLEAR_INCOME | OBJECT_FLAG_HAS_NO_FOUNDATION ), // Only non-silly use case is to use it when you cannot build a station, so disallow bridges M(STR_LAI_OBJECT_DESCRIPTION_COMPANY_HEADQUARTERS, 0x22, 0, 0, 7, OBJECT_FLAG_CANNOT_REMOVE | OBJECT_FLAG_ONLY_IN_GAME), };