diff --git a/src/error.h b/src/error.h index 206bb67fbd..eeb90557d3 100644 --- a/src/error.h +++ b/src/error.h @@ -13,6 +13,8 @@ #define ERROR_H #include "strings_type.h" +#include "company_type.h" +#include "core/geometry_type.hpp" /** Message severity/type */ enum WarningLevel { @@ -22,6 +24,30 @@ enum WarningLevel { WL_CRITICAL, ///< Critical errors, the MessageBox is shown in all cases }; +/** The data of the error message. */ +class ErrorMessageData { +protected: + uint duration; ///< Length of display of the message. 0 means forever, + uint64 decode_params[20]; ///< Parameters of the message strings. + const char *strings[20]; ///< Copies of raw strings that were used. + uint textref_stack_size; ///< Number of uint32 values to put on the #TextRefStack for the error message. + uint32 textref_stack[16]; ///< Values to put on the #TextRefStack for the error message. + StringID summary_msg; ///< General error message showed in first line. Must be valid. + StringID detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. + Point position; ///< Position of the error message window. + CompanyID face; ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present. + +public: + ErrorMessageData(const ErrorMessageData &data); + ~ErrorMessageData(); + ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration = 0, int x = 0, int y = 0, uint textref_stack_size = 0, const uint32 *textref_stack = NULL); + + void SetDParam(uint n, uint64 v); + void SetDParamStr(uint n, const char *str); + + void CopyOutDParams(); +}; + void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0, uint textref_stack_size = 0, const uint32 *textref_stack = NULL); void ClearErrorMessages(); void ShowFirstError(); diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 4d81f753f9..b054f8834b 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -66,72 +66,96 @@ static const WindowDesc _errmsg_face_desc( _nested_errmsg_face_widgets, lengthof(_nested_errmsg_face_widgets) ); -/** The data of the error message. */ -class ErrorMessageData { -protected: - uint duration; ///< Length of display of the message. 0 means forever, - uint64 decode_params[20]; ///< Parameters of the message strings. - const char *strings[20]; ///< Copies of raw strings that were used. - uint textref_stack_size; ///< Number of uint32 values to put on the #TextRefStack for the error message. - uint32 textref_stack[16]; ///< Values to put on the #TextRefStack for the error message. - StringID summary_msg; ///< General error message showed in first line. Must be valid. - StringID detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. - Point position; ///< Position of the error message window. - CompanyID face; ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present. - -public: - /** - * Copy the given data into our instace. - * @param data The data to copy. - */ - ErrorMessageData(const ErrorMessageData &data) - { - *this = data; - for (size_t i = 0; i < lengthof(this->strings); i++) { - if (this->strings[i] != NULL) { - this->strings[i] = strdup(this->strings[i]); - this->decode_params[i] = (size_t)this->strings[i]; - } +/** + * Copy the given data into our instace. + * @param data The data to copy. + */ +ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) +{ + *this = data; + for (size_t i = 0; i < lengthof(this->strings); i++) { + if (this->strings[i] != NULL) { + this->strings[i] = strdup(this->strings[i]); + this->decode_params[i] = (size_t)this->strings[i]; } } +} - /** Free all the strings. */ - ~ErrorMessageData() - { - for (size_t i = 0; i < lengthof(this->strings); i++) free(this->strings[i]); - } +/** Free all the strings. */ +ErrorMessageData::~ErrorMessageData() +{ + for (size_t i = 0; i < lengthof(this->strings); i++) free(this->strings[i]); +} - /** - * Display an error message in a window. - * @param summary_msg General error message showed in first line. Must be valid. - * @param detailed_msg Detailed error message showed in second line. Can be INVALID_STRING_ID. - * @param duration The amount of time to show this error message. - * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. - * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. - * @param textref_stack_size Number of uint32 values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. - * @param textref_stack Values to put on the #TextRefStack. - */ - ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration, int x, int y, uint textref_stack_size, const uint32 *textref_stack) : - duration(duration), - textref_stack_size(textref_stack_size), - summary_msg(summary_msg), - detailed_msg(detailed_msg) - { - this->position.x = x; - this->position.y = y; - if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_size, textref_stack); - CopyOutDParam(this->decode_params, this->strings, detailed_msg == INVALID_STRING_ID ? summary_msg : detailed_msg, lengthof(this->decode_params)); - if (textref_stack_size > 0) { - StopTextRefStackUsage(); - MemCpyT(this->textref_stack, textref_stack, textref_stack_size); - } +/** + * Display an error message in a window. + * @param summary_msg General error message showed in first line. Must be valid. + * @param detailed_msg Detailed error message showed in second line. Can be INVALID_STRING_ID. + * @param duration The amount of time to show this error message. + * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. + * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. + * @param textref_stack_size Number of uint32 values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. + * @param textref_stack Values to put on the #TextRefStack. + */ +ErrorMessageData::ErrorMessageData(StringID summary_msg, StringID detailed_msg, uint duration, int x, int y, uint textref_stack_size, const uint32 *textref_stack) : + duration(duration), + textref_stack_size(textref_stack_size), + summary_msg(summary_msg), + detailed_msg(detailed_msg), + face(INVALID_COMPANY) +{ + this->position.x = x; + this->position.y = y; + memset(this->decode_params, 0, sizeof(this->decode_params)); + memset(this->strings, 0, sizeof(this->strings)); + + if (textref_stack_size > 0) MemCpyT(this->textref_stack, textref_stack, textref_stack_size); + + assert(summary_msg != INVALID_STRING_ID); +} + +/** + * Copy error parameters from current DParams. + */ +void ErrorMessageData::CopyOutDParams() +{ + /* Reset parameters */ + for (size_t i = 0; i < lengthof(this->strings); i++) free(this->strings[i]); + memset(this->decode_params, 0, sizeof(this->decode_params)); + memset(this->strings, 0, sizeof(this->strings)); + + /* Get parameters using type information */ + if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_size, this->textref_stack); + CopyOutDParam(this->decode_params, this->strings, this->detailed_msg == INVALID_STRING_ID ? this->summary_msg : this->detailed_msg, lengthof(this->decode_params)); + if (this->textref_stack_size > 0) StopTextRefStackUsage(); + + if (this->detailed_msg == STR_ERROR_OWNED_BY) { CompanyID company = (CompanyID)GetDParamX(this->decode_params, 2); - this->face = (this->detailed_msg == STR_ERROR_OWNED_BY && company < MAX_COMPANIES) ? company : INVALID_COMPANY; - - assert(summary_msg != INVALID_STRING_ID); + if (company < MAX_COMPANIES) face = company; } -}; +} + +/** + * Set a error string parameter. + * @param n Parameter index + * @param v Parameter value + */ +void ErrorMessageData::SetDParam(uint n, uint64 v) +{ + this->decode_params[n] = v; +} + +/** + * Set a rawstring parameter. + * @param n Parameter index + * @param str Raw string + */ +void ErrorMessageData::SetDParamStr(uint n, const char *str) +{ + free(this->strings[n]); + this->strings[n] = strdup(str); +} /** Define a queue with errors. */ typedef std::list ErrorList; @@ -367,6 +391,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel if (_settings_client.gui.errmsg_duration == 0 && !no_timeout) return; ErrorMessageData data(summary_msg, detailed_msg, no_timeout ? 0 : _settings_client.gui.errmsg_duration, x, y, textref_stack_size, textref_stack); + data.CopyOutDParams(); ErrmsgWindow *w = (ErrmsgWindow*)FindWindowById(WC_ERRMSG, 0); if (w != NULL && w->IsCritical()) { @@ -382,3 +407,13 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel new ErrmsgWindow(data); } } + +/** + * Schedule a list of errors. + * Note: This does not try to display the error now. This is useful if the window system is not yet running. + * @param data Error message datas; cleared afterwards + */ +void ScheduleErrorMessage(ErrorList &datas) +{ + _error_list.splice(_error_list.end(), datas); +} diff --git a/src/object_gui.cpp b/src/object_gui.cpp index 136bdf091b..390adc8d0a 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -192,8 +192,6 @@ public: } case WID_BO_SELECT_IMAGE: { - if (_selected_object_index < 0) break; - int obj_index = GB(widget, 16, 16); const ObjectSpec *spec = ObjectClass::Get(_selected_object_class, obj_index); if (spec == NULL) break; diff --git a/src/settings.cpp b/src/settings.cpp index 06e691a0a3..7419911f07 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -77,6 +77,10 @@ GameSettings _settings_newgame; ///< Game settings for new games (updated from VehicleDefaultSettings _old_vds; ///< Used for loading default vehicles settings from old savegames char *_config_file; ///< Configuration file of OpenTTD +typedef std::list ErrorList; +static ErrorList _settings_error_list; ///< Errors while loading minimal settings. + + typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object); typedef void SettingDescProcList(IniFile *ini, const char *grpname, StringList *list); @@ -340,53 +344,59 @@ static const void *StringToVal(const SettingDescBase *desc, const char *orig_str { const char *str = orig_str == NULL ? "" : orig_str; switch (desc->cmd) { - case SDT_NUMX: { - char *end; - size_t val = strtoul(str, &end, 0); - if (end == str) { - SetDParamStr(0, str); - SetDParamStr(1, desc->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE, WL_CRITICAL); + case SDT_NUMX: { + char *end; + size_t val = strtoul(str, &end, 0); + if (end == str) { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, desc->name); + _settings_error_list.push_back(msg); + return desc->def; + } + if (*end != '\0') { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); + msg.SetDParamStr(0, desc->name); + _settings_error_list.push_back(msg); + } + return (void*)val; + } + case SDT_ONEOFMANY: { + size_t r = LookupOneOfMany(desc->many, str); + /* if the first attempt of conversion from string to the appropriate value fails, + * look if we have defined a converter from old value to new value. */ + if (r == (size_t)-1 && desc->proc_cnvt != NULL) r = desc->proc_cnvt(str); + if (r != (size_t)-1) return (void*)r; // and here goes converted value + + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, desc->name); + _settings_error_list.push_back(msg); return desc->def; } - if (*end != '\0') { - SetDParamStr(0, desc->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS, WL_CRITICAL); + case SDT_MANYOFMANY: { + size_t r = LookupManyOfMany(desc->many, str); + if (r != (size_t)-1) return (void*)r; + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, desc->name); + _settings_error_list.push_back(msg); + return desc->def; } - return (void*)val; - } - case SDT_ONEOFMANY: { - size_t r = LookupOneOfMany(desc->many, str); - /* if the first attempt of conversion from string to the appropriate value fails, - * look if we have defined a converter from old value to new value. */ - if (r == (size_t)-1 && desc->proc_cnvt != NULL) r = desc->proc_cnvt(str); - if (r != (size_t)-1) return (void*)r; // and here goes converted value + case SDT_BOOLX: { + if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return (void*)true; + if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return (void*)false; - SetDParamStr(0, str); - SetDParamStr(1, desc->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE, WL_CRITICAL); - return desc->def; - } - case SDT_MANYOFMANY: { - size_t r = LookupManyOfMany(desc->many, str); - if (r != (size_t)-1) return (void*)r; - SetDParamStr(0, str); - SetDParamStr(1, desc->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE, WL_CRITICAL); - return desc->def; - } - case SDT_BOOLX: - if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return (void*)true; - if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return (void*)false; + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); + msg.SetDParamStr(0, str); + msg.SetDParamStr(1, desc->name); + _settings_error_list.push_back(msg); + return desc->def; + } - SetDParamStr(0, str); - SetDParamStr(1, desc->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE, WL_CRITICAL); - return desc->def; - - case SDT_STRING: return orig_str; - case SDT_INTLIST: return str; - default: break; + case SDT_STRING: return orig_str; + case SDT_INTLIST: return str; + default: break; } return NULL; @@ -496,38 +506,43 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp ptr = GetVariableAddress(object, sld); switch (sdb->cmd) { - case SDT_BOOLX: // All four are various types of (integer) numbers - case SDT_NUMX: - case SDT_ONEOFMANY: - case SDT_MANYOFMANY: - Write_ValidateSetting(ptr, sd, (int32)(size_t)p); break; + case SDT_BOOLX: // All four are various types of (integer) numbers + case SDT_NUMX: + case SDT_ONEOFMANY: + case SDT_MANYOFMANY: + Write_ValidateSetting(ptr, sd, (int32)(size_t)p); + break; - case SDT_STRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - if (p != NULL) ttd_strlcpy((char*)ptr, (const char*)p, sld->length); - break; - case SLE_VAR_STR: - case SLE_VAR_STRQ: - free(*(char**)ptr); - *(char**)ptr = p == NULL ? NULL : strdup((const char*)p); - break; - case SLE_VAR_CHAR: if (p != NULL) *(char *)ptr = *(const char *)p; break; - default: NOT_REACHED(); - } - break; + case SDT_STRING: + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + if (p != NULL) ttd_strlcpy((char*)ptr, (const char*)p, sld->length); + break; + case SLE_VAR_STR: + case SLE_VAR_STRQ: + free(*(char**)ptr); + *(char**)ptr = p == NULL ? NULL : strdup((const char*)p); + break; + case SLE_VAR_CHAR: if (p != NULL) *(char *)ptr = *(const char *)p; break; + default: NOT_REACHED(); + } + break; - case SDT_INTLIST: { - if (!LoadIntList((const char*)p, ptr, sld->length, GetVarMemType(sld->conv))) { - SetDParamStr(0, sdb->name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); - } else if (sd->desc.proc_cnvt != NULL) { - sd->desc.proc_cnvt((const char*)p); + case SDT_INTLIST: { + if (!LoadIntList((const char*)p, ptr, sld->length, GetVarMemType(sld->conv))) { + ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY); + msg.SetDParamStr(0, sdb->name); + _settings_error_list.push_back(msg); + + /* Use default */ + LoadIntList((const char*)sdb->def, ptr, sld->length, GetVarMemType(sld->conv)); + } else if (sd->desc.proc_cnvt != NULL) { + sd->desc.proc_cnvt((const char*)p); + } + break; } - break; - } - default: NOT_REACHED(); + default: NOT_REACHED(); } } } @@ -1626,6 +1641,11 @@ void LoadFromConfig(bool minimal) HandleOldDiffCustom(false); ValidateSettings(); + + /* Display sheduled errors */ + extern void ScheduleErrorMessage(ErrorList &datas); + ScheduleErrorMessage(_settings_error_list); + if (FindWindowById(WC_ERRMSG, 0) == NULL) ShowFirstError(); } delete ini; diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 4b705fc94d..202f2f0281 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -79,6 +79,7 @@ struct MemBlock { static uint _sprite_lru_counter; static MemBlock *_spritecache_ptr; +static uint _allocated_sprite_cache_size = 0; static int _compact_cache_counter; static void CompactSpriteCache(); @@ -843,10 +844,17 @@ void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator) static void GfxInitSpriteCache() { /* initialize sprite cache heap */ - if (_spritecache_ptr == NULL) _spritecache_ptr = (MemBlock*)MallocT(_sprite_cache_size * 1024 * 1024); + int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); + uint target_size = _sprite_cache_size * 1024 * 1024 * max(1, bpp / 8); + + if (_spritecache_ptr == NULL || _allocated_sprite_cache_size != target_size) { + free(_spritecache_ptr); + _allocated_sprite_cache_size = target_size; + _spritecache_ptr = (MemBlock*)MallocT(_allocated_sprite_cache_size); + } /* A big free block */ - _spritecache_ptr->size = ((_sprite_cache_size * 1024 * 1024) - sizeof(MemBlock)) | S_FREE_MASK; + _spritecache_ptr->size = (_allocated_sprite_cache_size - sizeof(MemBlock)) | S_FREE_MASK; /* Sentinel block (identified by size == 0) */ NextBlock(_spritecache_ptr)->size = 0; } diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index d9206d838b..69045664eb 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -212,10 +212,10 @@ var = _freetype.mono_aa def = false [SDTG_VAR] -name = ""max_sprite_cache_size"" +name = ""sprite_cache_size_px"" type = SLE_UINT var = _sprite_cache_size -def = 64 +def = 128 min = 1 max = 512