diff --git a/src/ai/api/ai_marine.cpp b/src/ai/api/ai_marine.cpp index 07de889da1..532d04d4df 100644 --- a/src/ai/api/ai_marine.cpp +++ b/src/ai/api/ai_marine.cpp @@ -77,7 +77,7 @@ EnforcePrecondition(false, ::IsValidTile(front)); EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile))); - return AIObject::DoCommand(tile, ::TileY(front) == ::TileY(tile), 0, CMD_BUILD_SHIP_DEPOT); + return AIObject::DoCommand(tile, ::TileX(front) == ::TileX(tile), 0, CMD_BUILD_SHIP_DEPOT); } /* static */ bool AIMarine::BuildDock(TileIndex tile, StationID station_id) diff --git a/src/ai/api/ai_marine.hpp b/src/ai/api/ai_marine.hpp index 19bc4b1d98..c97961d563 100644 --- a/src/ai/api/ai_marine.hpp +++ b/src/ai/api/ai_marine.hpp @@ -106,6 +106,7 @@ public: * @exception AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER * @return Whether the water depot has been/can be build or not. * @note A WaterDepot is 1 tile in width, and 2 tiles in length. + * @note The depot will be built towards the south from 'tile', not necessarily towards 'front'. */ static bool BuildWaterDepot(TileIndex tile, TileIndex front); diff --git a/src/fontcache.h b/src/fontcache.h index affc2606c4..b4bdc7a993 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -53,6 +53,7 @@ uint GetGlyphWidth(FontSize size, uint32 key); * @param settings the settings to overwrite the fontname of. * @param language_isocode the language, e.g. en_GB. * @param winlangid the language ID windows style. + * @param str Sample string. * @return true if a font has been set, false otherwise. */ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str); diff --git a/src/gfx.cpp b/src/gfx.cpp index c8e9229f20..d84ab8b3c5 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1645,22 +1645,37 @@ bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int heigh return true; } -static void SetCursorSprite(CursorID cursor, PaletteID pal) +/** + * Update cursor dimension. + * Called when changing cursor sprite resp. reloading grfs. + */ +void UpdateCursorSize() { CursorVars *cv = &_cursor; - const Sprite *p; + const Sprite *p = GetSprite(GB(cv->sprite, 0, SPRITE_WIDTH), ST_NORMAL); - if (cv->sprite == cursor) return; - - p = GetSprite(GB(cursor, 0, SPRITE_WIDTH), ST_NORMAL); - cv->sprite = cursor; - cv->pal = pal; cv->size.y = p->height; cv->size.x = p->width; cv->offs.x = p->x_offs; cv->offs.y = p->y_offs; cv->dirty = true; +} + +/** + * Switch cursor to different sprite. + * @param cursor Sprite to draw for the cursor. + * @param pal Palette to use for recolouring. + */ +static void SetCursorSprite(CursorID cursor, PaletteID pal) +{ + CursorVars *cv = &_cursor; + if (cv->sprite == cursor) return; + + cv->sprite = cursor; + cv->pal = pal; + UpdateCursorSize(); + cv->short_vehicle_offset = 0; } @@ -1682,6 +1697,12 @@ void CursorTick() SwitchAnimatedCursor(); } +/** + * Assign a single non-animated sprite to the cursor. + * @param sprite Sprite to draw for the cursor. + * @param pal Palette to use for recolouring. + * @see SetAnimatedMouseCursor + */ void SetMouseCursor(CursorID sprite, PaletteID pal) { /* Turn off animation */ @@ -1690,6 +1711,11 @@ void SetMouseCursor(CursorID sprite, PaletteID pal) SetCursorSprite(sprite, pal); } +/** + * Assign an animation to the cursor. + * @param table Array of animation states. + * @see SetMouseCursor + */ void SetAnimatedMouseCursor(const AnimCursor *table) { _cursor.animate_list = table; diff --git a/src/gfx_func.h b/src/gfx_func.h index 5f7bee2fae..5bcdd48302 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -152,6 +152,7 @@ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom); void SetMouseCursor(CursorID cursor, PaletteID pal); void SetAnimatedMouseCursor(const AnimCursor *table); void CursorTick(); +void UpdateCursorSize(); bool ChangeResInGame(int w, int h); void SortResolutions(int count); bool ToggleFullScreen(bool fs); diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 998ba3128a..5cda582b4b 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -201,6 +201,8 @@ void GfxLoadSprites() GfxInitSpriteMem(); LoadSpriteTables(); GfxInitPalettes(); + + UpdateCursorSize(); } bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index c165ae5387..6de0309f68 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -206,6 +206,7 @@ public: this->callback_timer = DAY_TICKS; this->InitNested(&_build_industry_desc, 0); + this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]); } virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) diff --git a/src/saveload/oldloader.cpp b/src/saveload/oldloader.cpp index c7cff72798..f921945279 100644 --- a/src/saveload/oldloader.cpp +++ b/src/saveload/oldloader.cpp @@ -21,6 +21,7 @@ #include "saveload_internal.h" #include "oldloader.h" +#include enum { TTO_HEADER_SIZE = 41, @@ -51,13 +52,14 @@ static byte ReadByteFromFile(LoadgameState *ls) /* To avoid slow reads, we read BUFFER_SIZE of bytes per time and just return a byte per time */ if (ls->buffer_cur >= ls->buffer_count) { + /* Read some new bytes from the file */ int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file); /* We tried to read, but there is nothing in the file anymore.. */ if (count == 0) { DEBUG(oldloader, 0, "Read past end of file, loading failed"); - ls->failed = true; + throw std::exception(); } ls->buffer_count = count; @@ -121,8 +123,6 @@ bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks) if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr; for (uint i = 0; i < chunk->amount; i++) { - if (ls->failed) return false; - /* Handle simple types */ if (GetOldChunkType(chunk->type) != 0) { switch (GetOldChunkType(chunk->type)) { @@ -137,7 +137,7 @@ bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks) case OC_ASSERT: DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value); - if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true; + if (ls->total_read != chunk->offset + _bump_assert_value) throw std::exception(); default: break; } } else { @@ -191,7 +191,6 @@ static void InitLoading(LoadgameState *ls) { ls->chunk_size = 0; ls->total_read = 0; - ls->failed = false; ls->decoding = false; ls->decode_char = 0; @@ -303,7 +302,14 @@ bool LoadOldSaveGame(const char *file) _savegame_type = type; - if (proc == NULL || !proc(&ls)) { + bool game_loaded; + try { + game_loaded = proc != NULL && proc(&ls); + } catch (...) { + game_loaded = false; + } + + if (!game_loaded) { SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED); fclose(ls.file); return false; diff --git a/src/saveload/oldloader.h b/src/saveload/oldloader.h index d1908e1364..8bddcad17f 100644 --- a/src/saveload/oldloader.h +++ b/src/saveload/oldloader.h @@ -33,7 +33,6 @@ struct LoadgameState { byte buffer[BUFFER_SIZE]; uint total_read; - bool failed; }; /* OldChunk-Type */ diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index e45a7876a4..777fa25899 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -1479,7 +1479,7 @@ static bool LoadOldMapPart1(LoadgameState *ls, int num) } } - return !ls->failed; + return true; } static bool LoadOldMapPart2(LoadgameState *ls, int num) @@ -1493,7 +1493,7 @@ static bool LoadOldMapPart2(LoadgameState *ls, int num) _m[i].m5 = ReadByte(ls); } - return !ls->failed; + return true; } static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num) @@ -1549,7 +1549,7 @@ static bool LoadTTDPatchExtraChunks(LoadgameState *ls, int num) } } - return !ls->failed; + return true; } extern TileIndex _cur_tileloop_tile; @@ -1733,11 +1733,17 @@ bool LoadTTDMain(LoadgameState *ls) SmallStackSafeStackAlloc map3; _old_map3 = map3.data; _old_vehicle_names = NULL; - if (!LoadChunk(ls, NULL, main_chunk)) { - DEBUG(oldloader, 0, "Loading failed"); + try { + if (!LoadChunk(ls, NULL, main_chunk)) { + DEBUG(oldloader, 0, "Loading failed"); + free(_old_vehicle_names); + return false; + } + } catch (...) { free(_old_vehicle_names); - return false; + throw; } + DEBUG(oldloader, 3, "Done, converting game data..."); FixTTDMapArray(); diff --git a/src/strings.cpp b/src/strings.cpp index 834bb52719..8a8c167cc8 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1527,6 +1527,38 @@ const char *GetCurrentLanguageIsoCode() return _langpack->isocode; } +/** + * Check whether there are glyphs missing in the current language. + * @param Pointer to an address for storing the text pointer. + * @return If glyphs are missing, return \c true, else return \false. + * @pre *str must not be \c NULL. + * @post If \c true is returned, *str points to a string that is found to contain at least one missing glyph. + */ +static bool FindMissingGlyphs(const char **str) +{ + const Sprite *question_mark = GetGlyph(FS_NORMAL, '?'); + for (uint i = 0; i != 32; i++) { + for (uint j = 0; j < _langtab_num[i]; j++) { + const char *text = _langpack_offs[_langtab_start[i] + j]; + *str = text; + for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) { + if (c == SCC_SETX) { + /* SetX is, together with SetXY as special character that + * uses the next (two) characters as data points. We have + * to skip those, otherwise the UTF8 reading will go haywire. */ + text++; + } else if (c == SCC_SETXY) { + text += 2; + } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) { + /* The character is printable, but not in the normal font. This is the case we were testing for. */ + return true; + } + } + } + } + return false; +} + /** * Check whether the currently loaded language pack * uses characters that the currently loaded font @@ -1544,81 +1576,52 @@ void CheckForMissingGlyphsInLoadedLanguagePack() * automatically choose another font. This resets that choice. */ UninitFreeType(); InitFreeType(); - bool retry = false; #endif - for (;;) { - const Sprite *question_mark = GetGlyph(FS_NORMAL, '?'); - - for (uint i = 0; i != 32; i++) { - for (uint j = 0; j < _langtab_num[i]; j++) { - const char *string = _langpack_offs[_langtab_start[i] + j]; - WChar c; - while ((c = Utf8Consume(&string)) != '\0') { - if (c == SCC_SETX) { - /* - * SetX is, together with SetXY as special character that - * uses the next (two) characters as data points. We have - * to skip those, otherwise the UTF8 reading will go - * haywire. - */ - string++; - } else if (c == SCC_SETXY) { - string += 2; - } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) { + const char *str; + bool bad_font = FindMissingGlyphs(&str); #ifdef WITH_FREETYPE - if (!retry) { - /* We found an unprintable character... lets try whether we can - * find a fallback font that can print the characters in the - * current language. */ - retry = true; + if (bad_font) { + /* We found an unprintable character... lets try whether we can find + * a fallback font that can print the characters in the current language. */ + FreeTypeSettings backup; + memcpy(&backup, &_freetype, sizeof(backup)); - FreeTypeSettings backup; - memcpy(&backup, &_freetype, sizeof(backup)); + bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, str); + if (success) { + UninitFreeType(); + InitFreeType(); + } - bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, string); - if (success) { - UninitFreeType(); - InitFreeType(); - } + memcpy(&_freetype, &backup, sizeof(backup)); - memcpy(&_freetype, &backup, sizeof(backup)); - - if (success) continue; - } else { - /* Our fallback font does miss characters too, so keep the - * user chosen font as that is more likely to be any good than - * the wild guess we made */ - UninitFreeType(); - InitFreeType(); - } -#endif - /* - * The character is printable, but not in the normal font. - * This is the case we were testing for. In this case we - * have to show the error. As we do not want the string to - * be translated by the translators, we 'force' it into the - * binary and 'load' it via a BindCString. To do this - * properly we have to set the colour of the string, - * otherwise we end up with a lot of artefacts. The colour - * 'character' might change in the future, so for safety - * we just Utf8 Encode it into the string, which takes - * exactly three characters, so it replaces the "XXX" with - * the colour marker. - */ - static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this."); - Utf8Encode(err_str, SCC_YELLOW); - SetDParamStr(0, err_str); - ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0); - - /* Reset the font width */ - LoadStringWidthTable(); - return; - } - } + if (success) { + bad_font = FindMissingGlyphs(&str); + if (bad_font) { + /* Our fallback font does miss characters too, so keep the + * user chosen font as that is more likely to be any good than + * the wild guess we made */ + UninitFreeType(); + InitFreeType(); } } - break; + } +#endif + + if (bad_font) { + /* All attempts have failed. Display an error. As we do not want the string to be translated by + * the translators, we 'force' it into the binary and 'load' it via a BindCString. To do this + * properly we have to set the colour of the string, otherwise we end up with a lot of artefacts. + * The colour 'character' might change in the future, so for safety we just Utf8 Encode it into + * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */ + static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this."); + Utf8Encode(err_str, SCC_YELLOW); + SetDParamStr(0, err_str); + ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0); + + /* Reset the font width */ + LoadStringWidthTable(); + return; } /* Update the font with cache */