1 /***************************************************************************
2  *   Free Heroes of Might and Magic II: https://github.com/ihhub/fheroes2  *
3  *   Copyright (C) 2021                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 #include <array>
22 #include <cassert>
23 #include <cstring>
24 #include <map>
25 #include <vector>
26 
27 #include "agg.h"
28 #include "agg_file.h"
29 #include "agg_image.h"
30 #include "h2d.h"
31 #include "icn.h"
32 #include "image.h"
33 #include "image_tool.h"
34 #include "pal.h"
35 #include "screen.h"
36 #include "text.h"
37 #include "til.h"
38 #include "ui_language.h"
39 #include "ui_text.h"
40 
41 namespace
42 {
43     const std::array<const char *, TIL::LASTTIL> tilFileName = { "UNKNOWN", "CLOF32.TIL", "GROUND32.TIL", "STON.TIL" };
44 
45     std::vector<std::vector<fheroes2::Sprite>> _icnVsSprite( ICN::LASTICN );
46     std::vector<std::vector<std::vector<fheroes2::Image>>> _tilVsImage( TIL::LASTTIL );
47     const fheroes2::Sprite errorImage;
48 
49     const uint32_t headerSize = 6;
50 
51     std::map<int, std::vector<fheroes2::Sprite>> _icnVsScaledSprite;
52 
IsValidICNId(int id)53     bool IsValidICNId( int id )
54     {
55         return id >= 0 && static_cast<size_t>( id ) < _icnVsSprite.size();
56     }
57 
IsValidTILId(int id)58     bool IsValidTILId( int id )
59     {
60         return id >= 0 && static_cast<size_t>( id ) < _tilVsImage.size();
61     }
62 
createDigit(const int32_t width,const int32_t height,const std::vector<fheroes2::Point> & points)63     fheroes2::Image createDigit( const int32_t width, const int32_t height, const std::vector<fheroes2::Point> & points )
64     {
65         fheroes2::Image digit( width, height );
66         digit.reset();
67 
68         fheroes2::SetPixel( digit, points, 115 );
69         fheroes2::Blit( fheroes2::CreateContour( digit, 35 ), digit );
70 
71         return digit;
72     }
73 
addDigit(const fheroes2::Sprite & original,const fheroes2::Image & digit,const fheroes2::Point & offset)74     fheroes2::Image addDigit( const fheroes2::Sprite & original, const fheroes2::Image & digit, const fheroes2::Point & offset )
75     {
76         fheroes2::Image combined( original.width() + digit.width() + offset.x, original.height() + ( offset.y < 0 ? 0 : offset.y ) );
77         combined.reset();
78 
79         fheroes2::Copy( original, 0, 0, combined, 0, 0, original.width(), original.height() );
80         fheroes2::Blit( digit, 0, 0, combined, original.width() + offset.x, combined.height() - digit.height() + ( offset.y >= 0 ? 0 : offset.y ), digit.width(),
81                         digit.height() );
82 
83         return combined;
84     }
85 
populateCursorIcons(std::vector<fheroes2::Sprite> & output,const fheroes2::Sprite & origin,const std::vector<fheroes2::Image> & digits,const fheroes2::Point & offset)86     void populateCursorIcons( std::vector<fheroes2::Sprite> & output, const fheroes2::Sprite & origin, const std::vector<fheroes2::Image> & digits,
87                               const fheroes2::Point & offset )
88     {
89         output.emplace_back( origin );
90         for ( size_t i = 0; i < digits.size(); ++i ) {
91             output.emplace_back( addDigit( origin, digits[i], offset ) );
92             output.back().setPosition( output.back().width() - origin.width(), output.back().height() - origin.height() );
93         }
94     }
95 
replaceTranformPixel(fheroes2::Image & image,const int32_t position,const uint8_t value)96     void replaceTranformPixel( fheroes2::Image & image, const int32_t position, const uint8_t value )
97     {
98         if ( image.transform()[position] != 0 ) {
99             image.transform()[position] = 0;
100             image.image()[position] = value;
101         }
102     }
103 
104     // This class serves the purpose of preserving the original alphabet which is loaded from AGG files for cases when we generate new language alphabet.
105     class OriginalAlphabetPreserver
106     {
107     public:
preserve()108         void preserve()
109         {
110             if ( _isPreserved ) {
111                 return;
112             }
113 
114             fheroes2::AGG::GetICN( ICN::FONT, 0 );
115             fheroes2::AGG::GetICN( ICN::SMALFONT, 0 );
116 
117             _normalFont = _icnVsSprite[ICN::FONT];
118             _smallFont = _icnVsSprite[ICN::SMALFONT];
119 
120             _isPreserved = true;
121         }
122 
restore() const123         void restore() const
124         {
125             if ( !_isPreserved ) {
126                 return;
127             }
128 
129             // Restore the original font.
130             _icnVsSprite[ICN::FONT] = _normalFont;
131             _icnVsSprite[ICN::SMALFONT] = _smallFont;
132 
133             // Clear modified fonts.
134             _icnVsSprite[ICN::YELLOW_FONT].clear();
135             _icnVsSprite[ICN::YELLOW_SMALLFONT].clear();
136             _icnVsSprite[ICN::GRAY_FONT].clear();
137             _icnVsSprite[ICN::GRAY_SMALL_FONT].clear();
138             _icnVsSprite[ICN::WHITE_LARGE_FONT].clear();
139         }
140 
141     private:
142         bool _isPreserved = false;
143 
144         std::vector<fheroes2::Sprite> _normalFont;
145         std::vector<fheroes2::Sprite> _smallFont;
146     };
147 
148     OriginalAlphabetPreserver alphabetPreserver;
149 
generatePolishAlphabet()150     void generatePolishAlphabet()
151     {
152         for ( const int icnId : { ICN::FONT, ICN::SMALFONT } ) {
153             std::vector<fheroes2::Sprite> & original = _icnVsSprite[icnId];
154 
155             original.resize( 96 );
156             original.insert( original.end(), 128, original[0] );
157             original[108] = original[51];
158             original[111] = original[58];
159             original[124] = original[83];
160             original[127] = original[90];
161             original[131] = original[44];
162             original[133] = original[33];
163             original[143] = original[58];
164             original[147] = original[76];
165             original[153] = original[65];
166             original[159] = original[90];
167             original[166] = original[35];
168             original[170] = original[37];
169             original[177] = original[46];
170             original[179] = original[47];
171             original[198] = original[67];
172             original[202] = original[69];
173             original[209] = original[78];
174             original[211] = original[79];
175         }
176 
177         // TODO: modify newly added characters accordingly.
178     }
179 
generateGermanAlphabet()180     void generateGermanAlphabet()
181     {
182         for ( const int icnId : { ICN::FONT, ICN::SMALFONT } ) {
183             std::vector<fheroes2::Sprite> & original = _icnVsSprite[icnId];
184 
185             original.resize( 103 );
186             original[96] = original[65];
187             original[97] = original[79];
188             original[98] = original[85];
189             original[99] = original[34];
190             original[100] = original[33];
191             original[101] = original[47];
192             original[102] = original[53];
193         }
194 
195         // TODO: modify newly added characters accordingly.
196     }
197 
generateFrenchAlphabet()198     void generateFrenchAlphabet()
199     {
200         // Resize fonts.
201         for ( const int icnId : { ICN::FONT, ICN::SMALFONT } ) {
202             _icnVsSprite[icnId].resize( 96 );
203         }
204 
205         // Normal font.
206         {
207             std::vector<fheroes2::Sprite> & font = _icnVsSprite[ICN::FONT];
208 
209             font[3].resize( font[79].width(), font[79].height() + 3 );
210             font[3].reset();
211             fheroes2::Copy( font[79], 0, 0, font[3], 0, 3, font[79].width(), font[79].height() );
212             // generate ^ on the top.
213             fheroes2::Copy( font[3], 2, 3, font[3], 3, 0, 1, 1 );
214             fheroes2::Copy( font[3], 4, 3, font[3], 4, 0, 1, 1 );
215             fheroes2::Copy( font[3], 2, 3, font[3], 5, 0, 1, 1 );
216             fheroes2::Copy( font[3], 2, 3, font[3], 2, 1, 1, 1 );
217             fheroes2::Copy( font[3], 4, 3, font[3], 3, 1, 1, 1 );
218             fheroes2::Copy( font[3], 4, 3, font[3], 5, 1, 1, 1 );
219             fheroes2::Copy( font[3], 2, 3, font[3], 6, 1, 1, 1 );
220             font[3].setPosition( font[79].x(), font[79].y() - 3 );
221             fheroes2::updateShadow( font[3], { -1, 2 }, 2 );
222 
223             font[4].resize( font[85].width(), font[85].height() + 3 );
224             font[4].reset();
225             fheroes2::Copy( font[85], 0, 0, font[4], 0, 3, font[85].width(), font[85].height() );
226             fheroes2::Copy( font[3], 2, 0, font[4], 3, 0, 5, 2 );
227             font[4].setPosition( font[85].x(), font[85].y() - 3 );
228             fheroes2::updateShadow( font[4], { -1, 2 }, 2 );
229 
230             font[6].resize( font[85].width(), font[85].height() + 3 );
231             font[6].reset();
232             // generate -_ on the top.
233             fheroes2::Copy( font[85], 0, 0, font[6], 0, 3, font[85].width(), font[85].height() );
234             fheroes2::Copy( font[6], 2, 3, font[6], 4, 0, 1, 1 );
235             fheroes2::Copy( font[6], 2, 4, font[6], 5, 0, 1, 1 );
236             fheroes2::Copy( font[6], 2, 3, font[6], 5, 1, 1, 1 );
237             fheroes2::Copy( font[6], 2, 4, font[6], 6, 1, 1, 1 );
238             fheroes2::Copy( font[6], 3, 3, font[6], 7, 1, 1, 1 );
239             font[6].setPosition( font[85].x(), font[85].y() - 3 );
240             fheroes2::updateShadow( font[6], { -1, 2 }, 2 );
241 
242             font[10].resize( font[65].width(), font[65].height() + 3 );
243             font[10].reset();
244             fheroes2::Copy( font[65], 0, 0, font[10], 0, 3, font[65].width(), font[65].height() );
245             fheroes2::Copy( font[3], 2, 0, font[10], 2, 0, 5, 2 );
246             font[10].setPosition( font[65].x(), font[65].y() - 3 );
247             fheroes2::updateShadow( font[10], { -1, 2 }, 2 );
248 
249             font[28] = font[73];
250             font[28].image()[2] = 0;
251             font[28].transform()[2] = 1;
252             font[28].image()[2 + font[28].width()] = 0;
253             font[28].transform()[2 + font[28].width()] = 1;
254             fheroes2::Copy( font[28], 3, 0, font[28], 1, 0, 1, 2 );
255             fheroes2::updateShadow( font[28], { -1, 2 }, 2 );
256 
257             font[30] = font[73];
258             font[30].image()[1] = 0;
259             font[30].transform()[1] = 1;
260             font[30].image()[3] = 0;
261             font[30].transform()[3] = 1;
262             font[30].image()[2 + font[30].width()] = 0;
263             font[30].transform()[2 + font[30].width()] = 1;
264             fheroes2::Copy( font[30], 2, 0, font[30], 1, 1, 1, 1 );
265             fheroes2::Copy( font[30], 2, 0, font[30], 3, 1, 1, 1 );
266             fheroes2::updateShadow( font[30], { -1, 2 }, 2 );
267 
268             font[32].resize( font[65].width(), font[65].height() + 3 );
269             font[32].reset();
270             fheroes2::Copy( font[65], 0, 0, font[32], 0, 3, font[65].width(), font[65].height() );
271             font[32].setPosition( font[65].x(), font[65].y() - 3 );
272             fheroes2::Copy( font[6], 4, 0, font[32], 3, 0, 4, 2 );
273             fheroes2::updateShadow( font[32], { -1, 2 }, 2 );
274 
275             font[62].resize( font[67].width(), font[67].height() + 2 );
276             font[62].reset();
277             fheroes2::Copy( font[67], 0, 0, font[62], 0, 0, font[67].width(), font[67].height() );
278             fheroes2::Copy( font[67], 2, 1, font[62], 4, 7, 1, 1 );
279             fheroes2::Copy( font[67], 5, 6, font[62], 5, 7, 1, 1 );
280             fheroes2::Copy( font[67], 2, 6, font[62], 6, 7, 1, 1 );
281             fheroes2::Copy( font[67], 2, 1, font[62], 5, 8, 1, 1 );
282             fheroes2::Copy( font[67], 5, 6, font[62], 6, 8, 1, 1 );
283             fheroes2::Copy( font[67], 2, 1, font[62], 4, 9, 1, 1 );
284             fheroes2::Copy( font[67], 5, 6, font[62], 5, 9, 1, 1 );
285             fheroes2::Copy( font[67], 3, 0, font[62], 6, 9, 1, 1 );
286             font[62].setPosition( font[67].x(), font[67].y() );
287             fheroes2::updateShadow( font[62], { -1, 2 }, 2 );
288 
289             font[64].resize( font[69].width(), font[69].height() + 3 );
290             font[64].reset();
291             fheroes2::Copy( font[69], 0, 0, font[64], 0, 3, font[69].width(), font[69].height() );
292             fheroes2::Copy( font[64], 4, 3, font[64], 4, 0, 1, 1 );
293             fheroes2::Copy( font[64], 4, 3, font[64], 5, 1, 1, 1 );
294             fheroes2::Copy( font[64], 8, 6, font[64], 5, 0, 1, 1 );
295             fheroes2::Copy( font[64], 8, 6, font[64], 6, 1, 1, 1 );
296             fheroes2::Copy( font[64], 4, 8, font[64], 6, 2, 1, 1 );
297             font[64].setPosition( font[69].x(), font[69].y() - 3 );
298             fheroes2::updateShadow( font[64], { -1, 2 }, 2 );
299 
300             font[91] = font[28];
301 
302             font[92].resize( font[69].width(), font[69].height() + 3 );
303             font[92].reset();
304             fheroes2::Copy( font[69], 0, 0, font[92], 0, 3, font[69].width(), font[69].height() );
305             fheroes2::Copy( font[3], 2, 0, font[92], 3, 0, 5, 2 );
306             font[92].setPosition( font[69].x(), font[69].y() - 3 );
307             fheroes2::updateShadow( font[92], { -1, 2 }, 2 );
308 
309             font[93] = font[30];
310 
311             font[94].resize( font[69].width(), font[69].height() + 3 );
312             font[94].reset();
313             fheroes2::Copy( font[69], 0, 0, font[94], 0, 3, font[69].width(), font[69].height() );
314             fheroes2::Copy( font[94], 4, 8, font[94], 3, 1, 1, 1 );
315             fheroes2::Copy( font[94], 8, 6, font[94], 4, 1, 1, 1 );
316             fheroes2::Copy( font[94], 8, 6, font[94], 5, 0, 1, 1 );
317             fheroes2::Copy( font[94], 4, 3, font[94], 6, 0, 1, 1 );
318             fheroes2::Copy( font[94], 4, 3, font[94], 5, 1, 1, 1 );
319             font[94].setPosition( font[69].x(), font[69].y() - 3 );
320             fheroes2::updateShadow( font[94], { -1, 2 }, 2 );
321 
322             font[95] = font[30];
323         }
324 
325         // Small font.
326         {
327             std::vector<fheroes2::Sprite> & font = _icnVsSprite[ICN::SMALFONT];
328 
329             font[3].resize( font[79].width(), font[79].height() + 2 );
330             font[3].reset();
331             fheroes2::Copy( font[79], 0, 0, font[3], 0, 2, font[79].width(), font[79].height() );
332             font[3].setPosition( font[79].x(), font[79].y() - 2 );
333             fheroes2::Copy( font[3], 2, 2, font[3], 2, 0, 1, 1 );
334             fheroes2::Copy( font[3], 2, 2, font[3], 4, 0, 1, 1 );
335             fheroes2::updateShadow( font[3], { -1, 1 }, 2 );
336 
337             font[4].resize( font[85].width(), font[85].height() + 2 );
338             font[4].reset();
339             fheroes2::Copy( font[85], 0, 0, font[4], 0, 2, font[85].width(), font[85].height() );
340             font[4].setPosition( font[85].x(), font[85].y() - 2 );
341             fheroes2::Copy( font[4], 1, 2, font[4], 3, 0, 1, 1 );
342             fheroes2::Copy( font[4], 1, 2, font[4], 5, 0, 1, 1 );
343             fheroes2::updateShadow( font[4], { -1, 1 }, 2 );
344 
345             font[6].resize( font[85].width(), font[85].height() + 2 );
346             font[6].reset();
347             fheroes2::Copy( font[85], 0, 0, font[6], 0, 2, font[85].width(), font[85].height() );
348             font[6].setPosition( font[85].x(), font[85].y() - 2 );
349             fheroes2::Copy( font[6], 1, 2, font[6], 4, 0, 1, 1 );
350             fheroes2::updateShadow( font[6], { -1, 1 }, 2 );
351 
352             font[10].resize( font[65].width(), font[65].height() + 2 );
353             font[10].reset();
354             fheroes2::Copy( font[65], 0, 0, font[10], 0, 2, font[65].width(), font[65].height() );
355             font[10].setPosition( font[65].x(), font[65].y() - 2 );
356             fheroes2::Copy( font[10], 2, 2, font[10], 2, 1, 1, 1 );
357             fheroes2::Copy( font[10], 2, 2, font[10], 4, 1, 1, 1 );
358             fheroes2::Copy( font[10], 2, 2, font[10], 3, 0, 1, 1 );
359             fheroes2::updateShadow( font[10], { -1, 1 }, 2 );
360 
361             font[28] = font[73];
362             fheroes2::FillTransform( font[28], 0, 0, font[28].width(), 2, 1 );
363             fheroes2::Copy( font[28], 1, 2, font[28], 1, 0, 1, 1 );
364             fheroes2::Copy( font[28], 1, 2, font[28], 3, 0, 1, 1 );
365             fheroes2::updateShadow( font[28], { -1, 1 }, 2 );
366 
367             font[30] = font[28];
368 
369             font[32].resize( font[65].width(), font[65].height() + 2 );
370             font[32].reset();
371             fheroes2::Copy( font[65], 0, 0, font[32], 0, 2, font[65].width(), font[65].height() );
372             font[32].setPosition( font[65].x(), font[65].y() - 2 );
373             fheroes2::Copy( font[32], 2, 2, font[32], 3, 0, 1, 1 );
374             fheroes2::Copy( font[32], 2, 2, font[32], 4, 1, 1, 1 );
375             fheroes2::updateShadow( font[32], { -1, 1 }, 2 );
376 
377             font[62].resize( font[67].width(), font[67].height() + 2 );
378             font[62].reset();
379             fheroes2::Copy( font[67], 0, 0, font[62], 0, 0, font[67].width(), font[67].height() );
380             fheroes2::Copy( font[62], 3, 4, font[62], 3, 5, 1, 1 );
381             fheroes2::Copy( font[62], 3, 4, font[62], 2, 6, 1, 1 );
382             fheroes2::updateShadow( font[62], { -1, 1 }, 2 );
383 
384             font[64].resize( font[69].width(), font[69].height() + 2 );
385             font[64].reset();
386             fheroes2::Copy( font[69], 0, 0, font[64], 0, 2, font[69].width(), font[69].height() );
387             font[64].setPosition( font[69].x(), font[69].y() - 2 );
388             fheroes2::Copy( font[64], 2, 2, font[64], 2, 0, 1, 1 );
389             fheroes2::Copy( font[64], 2, 2, font[64], 3, 1, 1, 1 );
390             fheroes2::updateShadow( font[64], { -1, 1 }, 2 );
391 
392             font[91] = font[28];
393 
394             font[92].resize( font[69].width(), font[69].height() + 2 );
395             font[92].reset();
396             fheroes2::Copy( font[69], 0, 0, font[92], 0, 2, font[69].width(), font[69].height() );
397             font[92].setPosition( font[69].x(), font[69].y() - 2 );
398             fheroes2::Copy( font[92], 2, 2, font[92], 3, 0, 1, 1 );
399             fheroes2::Copy( font[92], 2, 2, font[92], 2, 1, 1, 1 );
400             fheroes2::Copy( font[92], 2, 2, font[92], 4, 1, 1, 1 );
401             fheroes2::updateShadow( font[92], { -1, 1 }, 2 );
402 
403             font[93] = font[28];
404 
405             font[94].resize( font[69].width(), font[69].height() + 2 );
406             font[94].reset();
407             fheroes2::Copy( font[69], 0, 0, font[94], 0, 2, font[69].width(), font[69].height() );
408             font[94].setPosition( font[69].x(), font[69].y() - 2 );
409             fheroes2::Copy( font[94], 2, 2, font[94], 4, 0, 1, 1 );
410             fheroes2::Copy( font[94], 2, 2, font[94], 3, 1, 1, 1 );
411             fheroes2::updateShadow( font[94], { -1, 1 }, 2 );
412 
413             font[95] = font[28];
414         }
415     }
416 
generateRussianAlphabet()417     void generateRussianAlphabet()
418     {
419         // Resize fonts.
420         for ( const int icnId : { ICN::FONT, ICN::SMALFONT } ) {
421             std::vector<fheroes2::Sprite> & original = _icnVsSprite[icnId];
422 
423             original.resize( 96 );
424             original.insert( original.end(), 128, original[0] );
425         }
426 
427         // Normal font.
428         {
429             std::vector<fheroes2::Sprite> & font = _icnVsSprite[ICN::FONT];
430 
431             size_t offset = 0;
432 
433             // E with 2 dots on top.
434             font[168 - 32].resize( font[37 + offset].width(), font[37 + offset].height() + 3 );
435             font[168 - 32].reset();
436             fheroes2::Copy( font[37 + offset], 0, 0, font[168 - 32], 0, 3, font[37 + offset].width(), font[37 + offset].height() );
437             fheroes2::Copy( font[168 - 32], 5, 5, font[168 - 32], 4, 0, 1, 1 );
438             fheroes2::Copy( font[168 - 32], 5, 5, font[168 - 32], 7, 0, 1, 1 );
439             fheroes2::Copy( font[168 - 32], 4, 5, font[168 - 32], 4, 1, 1, 1 );
440             fheroes2::Copy( font[168 - 32], 4, 5, font[168 - 32], 7, 1, 1, 1 );
441             font[168 - 32].setPosition( font[37 + offset].x(), font[37 + offset].y() - 3 );
442             fheroes2::updateShadow( font[168 - 32], { -1, 2 }, 2 );
443 
444             font[192 - 32] = font[33];
445 
446             font[193 - 32] = font[34 + offset];
447             fheroes2::FillTransform( font[193 - 32], 9, 4, 2, 1, 1 );
448             fheroes2::Copy( font[38], 6, 0, font[193 - 32], 6, 0, 5, 4 );
449             fheroes2::Copy( font[193 - 32], 9, 5, font[193 - 32], 8, 4, 1, 1 );
450             fheroes2::updateShadow( font[193 - 32], { -1, 2 }, 2 );
451 
452             font[194 - 32] = font[34 + offset];
453 
454             font[195 - 32] = font[38];
455             fheroes2::FillTransform( font[195 - 32], 6, 4, 3, 4, 1 );
456 
457             font[196 - 32] = font[36 + offset];
458 
459             font[197 - 32] = font[37 + offset];
460 
461             // x with | in the middle.
462             font[198 - 32].resize( font[56].width() + 1, font[56].height() );
463             font[198 - 32].reset();
464             fheroes2::Copy( font[56], 1, 0, font[198 - 32], 1, 0, 8, 11 );
465             fheroes2::Copy( font[56], 9, 0, font[198 - 32], 10, 0, 6, 11 );
466             fheroes2::Fill( font[198 - 32], 9, 1, 1, 9, font[198 - 32].image()[1 + font[198 - 32].width()] );
467             font[198 - 32].setPosition( font[56].x(), font[56].y() );
468             fheroes2::updateShadow( font[198 - 32], { -1, 2 }, 2 );
469 
470             font[199 - 32].resize( font[19].width() + 1, font[19].height() );
471             font[199 - 32].reset();
472             fheroes2::Copy( font[19], 1, 0, font[199 - 32], 1, 0, 5, 3 );
473             fheroes2::Copy( font[19], 5, 0, font[199 - 32], 6, 0, 3, 4 );
474             fheroes2::Copy( font[19], 3, 5, font[199 - 32], 4, 4, 5, 4 );
475             fheroes2::Copy( font[19], 1, 8, font[199 - 32], 1, 8, 5, 3 );
476             fheroes2::Copy( font[19], 5, 8, font[199 - 32], 6, 8, 3, 3 );
477             fheroes2::FillTransform( font[199 - 32], 2, 6, 5, 3, 1 );
478             font[199 - 32].setPosition( font[19].x(), font[19].y() );
479             fheroes2::updateShadow( font[199 - 32], { -1, 2 }, 2 );
480 
481             // Reverted N.
482             font[200 - 32] = font[46];
483             fheroes2::FillTransform( font[200 - 32], 6, 1, 5, 11, 1 );
484             fheroes2::Copy( font[46], 6, 2, font[200 - 32], 6, 6, 1, 3 );
485             fheroes2::Copy( font[46], 7, 3, font[200 - 32], 7, 5, 1, 3 );
486             fheroes2::Copy( font[46], 8, 4, font[200 - 32], 8, 4, 1, 3 );
487             fheroes2::Copy( font[46], 8, 4, font[200 - 32], 9, 3, 1, 3 );
488             fheroes2::Copy( font[46], 8, 4, font[200 - 32], 10, 2, 1, 3 );
489             fheroes2::Copy( font[46], 8, 4, font[200 - 32], 11, 1, 1, 3 );
490             fheroes2::Copy( font[46], 11, 7, font[200 - 32], 11, 8, 1, 1 );
491             fheroes2::Copy( font[46], 13, 9, font[200 - 32], 11, 9, 1, 1 );
492             fheroes2::updateShadow( font[200 - 32], { -1, 2 }, 2 );
493 
494             font[201 - 32].resize( font[200 - 32].width(), font[200 - 32].height() + 3 );
495             font[201 - 32].reset();
496             fheroes2::Copy( font[200 - 32], 0, 0, font[201 - 32], 0, 3, font[200 - 32].width(), font[200 - 32].height() );
497             font[201 - 32].setPosition( font[200 - 32].x(), font[200 - 32].y() - 3 );
498             fheroes2::Copy( font[201 - 32], 12, 4, font[201 - 32], 8, 0, 1, 1 );
499             fheroes2::Copy( font[201 - 32], 11, 10, font[201 - 32], 8, 1, 1, 1 );
500             fheroes2::updateShadow( font[201 - 32], { -1, 2 }, 2 );
501 
502             font[202 - 32] = font[43 + offset];
503 
504             font[204 - 32] = font[45 + offset];
505             font[205 - 32] = font[40 + offset];
506             font[206 - 32] = font[47 + offset];
507 
508             font[207 - 32] = font[195 - 32];
509             fheroes2::Copy( font[207 - 32], 4, 1, font[207 - 32], 8, 1, 2, 9 );
510             fheroes2::Copy( font[207 - 32], 4, 9, font[207 - 32], 8, 10, 2, 1 );
511             fheroes2::Copy( font[207 - 32], 6, 0, font[207 - 32], 10, 0, 1, 2 );
512             fheroes2::updateShadow( font[207 - 32], { -1, 2 }, 2 );
513 
514             font[203 - 32].resize( font[207 - 32].width() - 1, font[207 - 32].height() );
515             font[203 - 32].reset();
516             fheroes2::Copy( font[207 - 32], 0, 0, font[203 - 32], 0, 0, font[207 - 32].width() - 1, font[207 - 32].height() );
517             fheroes2::FillTransform( font[203 - 32], 0, 0, 4, 6, 1 );
518             fheroes2::FillTransform( font[203 - 32], 4, 0, 3, 2, 1 );
519             fheroes2::Copy( font[203 - 32], 4, 2, font[203 - 32], 5, 1, 2, 1 );
520             fheroes2::Copy( font[203 - 32], 1, 10, font[203 - 32], 5, 0, 2, 1 );
521             font[203 - 32].setPosition( font[207 - 32].x(), font[207 - 32].y() );
522             fheroes2::updateShadow( font[203 - 32], { -1, 2 }, 2 );
523 
524             font[208 - 32] = font[48 + offset];
525             font[209 - 32] = font[35 + offset];
526 
527             font[210 - 32].resize( font[207 - 32].width() + 4, font[207 - 32].height() );
528             font[210 - 32].reset();
529             fheroes2::Copy( font[207 - 32], 0, 0, font[210 - 32], 0, 0, font[207 - 32].width(), font[207 - 32].height() );
530             fheroes2::Copy( font[210 - 32], 7, 0, font[210 - 32], 11, 0, 4, font[207 - 32].height() );
531             font[210 - 32].setPosition( font[207 - 32].x(), font[207 - 32].y() );
532 
533             font[211 - 32] = font[57 + offset];
534 
535             font[212 - 32].resize( font[48].width() + 1, font[48].height() );
536             font[212 - 32].reset();
537             fheroes2::Copy( font[48], 0, 0, font[212 - 32], 1, 0, font[48].width(), font[48].height() );
538             {
539                 // TODO: add proper Flip function variant.
540                 fheroes2::Sprite temp = fheroes2::Crop( font[48], 6, 0, 5, 6 );
541                 temp = fheroes2::Flip( temp, true, false );
542                 fheroes2::Copy( temp, 0, 0, font[212 - 32], 1, 0, temp.width(), temp.height() );
543             }
544             font[212 - 32].setPosition( font[48].x(), font[48].y() );
545             fheroes2::updateShadow( font[212 - 32], { -1, 2 }, 2 );
546 
547             font[213 - 32] = font[56 + offset];
548 
549             font[214 - 32].resize( font[53].width() + 2, font[53].height() + 1 );
550             font[214 - 32].reset();
551             fheroes2::Copy( font[53], 0, 0, font[214 - 32], 0, 0, font[52].width(), font[52].height() );
552             fheroes2::Copy( font[214 - 32], 9, 1, font[214 - 32], 11, 9, 1, 1 );
553             fheroes2::Copy( font[214 - 32], 9, 1, font[214 - 32], 12, 8, 1, 1 );
554             fheroes2::Copy( font[214 - 32], 9, 1, font[214 - 32], 12, 10, 1, 2 );
555             fheroes2::Copy( font[214 - 32], 10, 1, font[214 - 32], 12, 9, 1, 1 );
556             fheroes2::Copy( font[214 - 32], 10, 1, font[214 - 32], 13, 8, 1, 4 );
557             font[214 - 32].setPosition( font[53].x(), font[53].y() );
558             fheroes2::updateShadow( font[214 - 32], { -1, 2 }, 2 );
559 
560             font[216 - 32].resize( font[53].width() + 2, font[53].height() );
561             font[216 - 32].reset();
562             fheroes2::Copy( font[53], 0, 0, font[216 - 32], 0, 0, 6, 11 );
563             fheroes2::Copy( font[53], 8, 0, font[216 - 32], 7, 0, 3, 11 );
564             fheroes2::Copy( font[53], 8, 0, font[216 - 32], 11, 0, 3, 11 );
565             fheroes2::Copy( font[204 - 32], 10, 0, font[216 - 32], 6, 5, 3, 5 );
566             fheroes2::Copy( font[204 - 32], 10, 0, font[216 - 32], 10, 5, 3, 5 );
567             fheroes2::FillTransform( font[216 - 32], 7, 10, 1, 1, 1 );
568             fheroes2::FillTransform( font[216 - 32], 11, 10, 1, 1, 1 );
569             font[216 - 32].setPosition( font[53].x(), font[53].y() );
570             fheroes2::updateShadow( font[216 - 32], { -1, 2 }, 2 );
571 
572             font[215 - 32] = font[53];
573             fheroes2::FillTransform( font[215 - 32], 3, 6, 6, 7, 1 );
574             fheroes2::Copy( font[216 - 32], 4, 5, font[215 - 32], 4, 3, 4, 6 );
575             fheroes2::Copy( font[215 - 32], 6, 5, font[215 - 32], 8, 3, 1, 3 );
576             fheroes2::Copy( font[215 - 32], 7, 4, font[215 - 32], 9, 2, 1, 2 );
577             fheroes2::Copy( font[215 - 32], 9, 8, font[215 - 32], 9, 9, 1, 1 );
578             fheroes2::updateShadow( font[215 - 32], { -1, 2 }, 2 );
579 
580             font[217 - 32].resize( font[216 - 32].width() + 2, font[216 - 32].height() + 1 );
581             font[217 - 32].reset();
582             fheroes2::Copy( font[216 - 32], 0, 0, font[217 - 32], 0, 0, font[216 - 32].width(), font[216 - 32].height() );
583             fheroes2::Copy( font[214 - 32], 11, 8, font[217 - 32], 14, 8, 3, 4 );
584             font[217 - 32].setPosition( font[216 - 32].x(), font[216 - 32].y() );
585             fheroes2::updateShadow( font[217 - 32], { -1, 2 }, 2 );
586 
587             font[218 - 32].resize( font[193 - 32].width() + 1, font[193 - 32].height() );
588             font[218 - 32].reset();
589             fheroes2::Copy( font[193 - 32], 0, 0, font[218 - 32], 1, 0, font[193 - 32].width(), font[193 - 32].height() );
590             fheroes2::Copy( font[193 - 32], 1, 0, font[218 - 32], 1, 0, 3, 4 );
591             fheroes2::FillTransform( font[218 - 32], 7, 0, 5, 4, 1 );
592             font[218 - 32].setPosition( font[193 - 32].x(), font[193 - 32].y() );
593             fheroes2::updateShadow( font[218 - 32], { -1, 2 }, 2 );
594 
595             font[220 - 32] = font[193 - 32];
596             fheroes2::FillTransform( font[220 - 32], 0, 0, 4, 6, 1 );
597             fheroes2::FillTransform( font[220 - 32], 6, 0, 5, 4, 1 );
598             fheroes2::Copy( font[53], 8, 0, font[220 - 32], 3, 0, 3, 1 );
599             fheroes2::updateShadow( font[220 - 32], { -1, 2 }, 2 );
600 
601             font[219 - 32].resize( font[220 - 32].width() + 3, font[220 - 32].height() );
602             font[219 - 32].reset();
603             fheroes2::Copy( font[220 - 32], 0, 0, font[219 - 32], 0, 0, font[220 - 32].width(), font[220 - 32].height() );
604             fheroes2::Copy( font[219 - 32], 3, 0, font[219 - 32], 11, 0, 3, 9 );
605             fheroes2::Copy( font[207 - 32], 8, 9, font[219 - 32], 12, 9, 2, 2 );
606             font[219 - 32].setPosition( font[220 - 32].x(), font[220 - 32].y() );
607             fheroes2::updateShadow( font[219 - 32], { -1, 2 }, 2 );
608 
609             font[221 - 32].resize( font[47].width() - 3, font[47].height() );
610             font[221 - 32].reset();
611             fheroes2::Copy( font[47], 4, 0, font[221 - 32], 1, 0, 9, 11 );
612             fheroes2::FillTransform( font[221 - 32], 0, 3, 3, 5, 1 );
613             fheroes2::Copy( font[221 - 32], 3, 0, font[221 - 32], 4, 5, 5, 1 );
614             font[221 - 32].setPosition( font[47].x(), font[47].y() );
615             fheroes2::updateShadow( font[221 - 32], { -1, 2 }, 2 );
616 
617             font[222 - 32].resize( font[47].width() + 1, font[47].height() );
618             font[222 - 32].reset();
619             fheroes2::Copy( font[193 - 32], 0, 0, font[222 - 32], 0, 0, 6, 13 );
620             fheroes2::Copy( font[47], 4, 1, font[222 - 32], 7, 1, 4, 8 );
621             fheroes2::Copy( font[47], 10, 1, font[222 - 32], 11, 1, 3, 8 );
622             fheroes2::Copy( font[47], 5, 0, font[222 - 32], 8, 0, 3, 1 );
623             fheroes2::Copy( font[47], 10, 0, font[222 - 32], 11, 0, 2, 1 );
624             fheroes2::Copy( font[47], 4, 9, font[222 - 32], 7, 9, 4, 2 );
625             fheroes2::Copy( font[47], 10, 9, font[222 - 32], 11, 9, 3, 2 );
626             fheroes2::Copy( font[222 - 32], 2, 0, font[222 - 32], 6, 5, 2, 1 );
627             font[222 - 32].setPosition( font[193 - 32].x(), font[193 - 32].y() );
628             fheroes2::updateShadow( font[222 - 32], { -1, 2 }, 2 );
629 
630             font[223 - 32].resize( font[203 - 32].width() - 1, font[203 - 32].height() );
631             font[223 - 32].reset();
632             fheroes2::Copy( font[33], 0, 5, font[223 - 32], 0, 5, 7, 6 );
633             fheroes2::Copy( font[212 - 32], 0, 0, font[223 - 32], 1, 0, 7, 6 );
634             fheroes2::Copy( font[203 - 32], 8, 0, font[223 - 32], 7, 0, 2, 11 );
635             fheroes2::Copy( font[223 - 32], 6, 5, font[223 - 32], 7, 5, 1, 1 );
636             font[223 - 32].setPosition( font[203 - 32].x(), font[203 - 32].y() );
637             fheroes2::updateShadow( font[223 - 32], { -1, 2 }, 2 );
638 
639             offset = 32;
640 
641             // e with 2 dots on top.
642             font[184 - 32] = font[37 + offset];
643 
644             font[224 - 32] = font[33 + offset];
645 
646             font[225 - 32].resize( font[69].width(), font[69].height() + 3 );
647             font[225 - 32].reset();
648             fheroes2::Copy( font[69], 1, 5, font[225 - 32], 1, 8, 8, 2 );
649             fheroes2::Copy( font[69], 1, 0, font[225 - 32], 1, 6, 8, 2 );
650             fheroes2::Copy( font[67], 1, 0, font[225 - 32], 1, 0, 8, 2 );
651             fheroes2::Copy( font[45], 7, 3, font[225 - 32], 1, 2, 3, 1 );
652             fheroes2::Copy( font[45], 7, 3, font[225 - 32], 2, 3, 3, 1 );
653             fheroes2::Copy( font[45], 7, 3, font[225 - 32], 3, 4, 3, 1 );
654             fheroes2::Copy( font[45], 8, 3, font[225 - 32], 6, 5, 2, 1 );
655             fheroes2::Copy( font[45], 7, 3, font[225 - 32], 4, 5, 2, 1 );
656             font[225 - 32].setPosition( font[69].x(), font[69].y() - 3 );
657             fheroes2::updateShadow( font[225 - 32], { -1, 2 }, 2 );
658 
659             font[227 - 32] = font[82];
660             fheroes2::Copy( font[227 - 32], 1, 0, font[227 - 32], 3, 0, 2, 1 );
661             fheroes2::Copy( font[227 - 32], 4, 2, font[227 - 32], 4, 1, 1, 1 );
662             fheroes2::SetTransformPixel( font[227 - 32], 4, 2, 1 );
663             fheroes2::updateShadow( font[227 - 32], { -1, 2 }, 2 );
664 
665             font[228 - 32] = font[71];
666             font[229 - 32] = font[37 + offset];
667 
668             // x with | in the middle.
669             font[230 - 32].resize( font[88].width() + 2, font[88].height() );
670             font[230 - 32].reset();
671             fheroes2::Copy( font[88], 0, 0, font[230 - 32], 0, 0, 6, 7 );
672             fheroes2::Copy( font[88], 5, 0, font[230 - 32], 7, 0, 5, 7 );
673             fheroes2::Fill( font[230 - 32], 6, 1, 1, 5, font[230 - 32].image()[3 + font[230 - 32].width()] );
674             font[230 - 32].setPosition( font[88].x(), font[88].y() );
675             fheroes2::updateShadow( font[230 - 32], { -1, 2 }, 2 );
676 
677             // letter 3 (z)
678             font[231 - 32].resize( font[19].width(), font[19].height() - 4 );
679             font[231 - 32].reset();
680             fheroes2::Copy( font[19], 0, 0, font[231 - 32], 0, 0, font[19].width(), 3 );
681             fheroes2::Copy( font[19], 0, 5, font[231 - 32], 0, 3, font[19].width(), 1 );
682             fheroes2::Copy( font[19], 0, 8, font[231 - 32], 0, 4, font[19].width(), 4 );
683             fheroes2::FillTransform( font[231 - 32], 0, 2, 3, 3, 1 );
684             font[231 - 32].setPosition( font[19].x(), font[19].y() + 4 );
685             fheroes2::updateShadow( font[231 - 32], { -1, 2 }, 2 );
686 
687             // letter B (v)
688             font[226 - 32].resize( font[231 - 32].width() + 1, font[231 - 32].height() );
689             font[226 - 32].reset();
690             fheroes2::Copy( font[231 - 32], 0, 0, font[226 - 32], 1, 0, font[231 - 32].width(), font[231 - 32].height() );
691             fheroes2::Copy( font[77], 1, 0, font[226 - 32], 1, 0, 3, 7 );
692             fheroes2::Copy( font[226 - 32], 7, 1, font[226 - 32], 3, 0, 1, 1 );
693             fheroes2::Copy( font[226 - 32], 7, 1, font[226 - 32], 3, 6, 1, 1 );
694             fheroes2::Copy( font[226 - 32], 3, 4, font[226 - 32], 3, 5, 1, 1 );
695             font[226 - 32].setPosition( font[231 - 32].x(), font[231 - 32].y() );
696             fheroes2::updateShadow( font[226 - 32], { -1, 2 }, 2 );
697 
698             font[232 - 32] = font[85];
699 
700             font[233 - 32].resize( font[232 - 32].width(), font[232 - 32].height() + 3 );
701             font[233 - 32].reset();
702             fheroes2::Copy( font[232 - 32], 0, 0, font[233 - 32], 0, 3, font[232 - 32].width(), font[232 - 32].height() );
703             fheroes2::Copy( font[233 - 32], 8, 3, font[233 - 32], 5, 0, 1, 1 );
704             fheroes2::Copy( font[233 - 32], 7, 3, font[233 - 32], 5, 1, 1, 1 );
705             font[233 - 32].setPosition( font[232 - 32].x(), font[232 - 32].y() - 3 );
706             fheroes2::updateShadow( font[233 - 32], { -1, 2 }, 2 );
707 
708             // Shorter k.
709             font[234 - 32].resize( font[75].width(), font[75].height() - 4 );
710             font[234 - 32].reset();
711             fheroes2::Copy( font[75], 2, 2, font[234 - 32], 2, 0, 7, 4 );
712             fheroes2::Copy( font[75], 1, 0, font[234 - 32], 1, 0, 3, 1 );
713             fheroes2::Copy( font[75], 0, 7, font[234 - 32], 0, 4, font[75].width(), 2 );
714             fheroes2::Copy( font[75], 0, 10, font[234 - 32], 0, 6, 4, 1 );
715             fheroes2::Copy( font[75], 7, 10, font[234 - 32], 6, 6, 3, 1 );
716             font[234 - 32].setPosition( font[75].x(), font[75].y() + 4 );
717             fheroes2::updateShadow( font[234 - 32], { -1, 2 }, 2 );
718 
719             font[235 - 32] = font[78];
720             fheroes2::Copy( font[235 - 32], 3, 0, font[235 - 32], 2, 1, 1, 1 );
721             fheroes2::FillTransform( font[235 - 32], 0, 0, 2, 3, 1 );
722             fheroes2::FillTransform( font[235 - 32], 2, 0, 1, 1, 1 );
723             fheroes2::updateShadow( font[235 - 32], { -1, 2 }, 2 );
724 
725             font[236 - 32] = font[45 + offset];
726             fheroes2::Copy( font[87], 9, 0, font[236 - 32], 3, 0, 4, 7 );
727             fheroes2::Copy( font[87], 9, 0, font[236 - 32], 9, 0, 4, 7 );
728             fheroes2::FillTransform( font[236 - 32], 0, 0, 3, 6, 1 );
729             fheroes2::updateShadow( font[236 - 32], { -1, 2 }, 2 );
730 
731             font[237 - 32] = font[78];
732             fheroes2::FillTransform( font[237 - 32], 4, 0, 3, 8, 1 );
733             fheroes2::Copy( font[78], 4, 1, font[237 - 32], 4, 3, 1, 2 );
734             fheroes2::Copy( font[78], 4, 1, font[237 - 32], 5, 3, 1, 2 );
735             fheroes2::Copy( font[78], 4, 1, font[237 - 32], 6, 3, 1, 2 );
736             fheroes2::Copy( font[78], 4, 1, font[237 - 32], 7, 3, 1, 1 );
737             fheroes2::updateShadow( font[237 - 32], { -1, 2 }, 2 );
738 
739             font[238 - 32] = font[47 + offset];
740 
741             font[239 - 32] = font[78];
742 
743             font[240 - 32] = font[48 + offset];
744             font[241 - 32] = font[35 + offset];
745             font[242 - 32] = font[77];
746             font[243 - 32] = font[57 + offset];
747 
748             font[244 - 32].resize( font[81].width(), font[81].height() );
749             font[244 - 32].reset();
750             fheroes2::Copy( font[80], 1, 0, font[244 - 32], 3, 0, 4, 10 );
751             fheroes2::Copy( font[81], 0, 0, font[244 - 32], 0, 0, 5, 7 );
752             fheroes2::Copy( font[80], 6, 0, font[244 - 32], 7, 0, 4, 7 );
753             font[244 - 32].setPosition( font[81].x(), font[81].y() );
754             fheroes2::updateShadow( font[244 - 32], { -1, 2 }, 2 );
755 
756             font[245 - 32] = font[56 + offset];
757 
758             font[246 - 32].resize( font[85].width() + 2, font[85].height() + 1 );
759             font[246 - 32].reset();
760             fheroes2::Copy( font[85], 0, 0, font[246 - 32], 0, 0, font[85].width(), font[85].height() );
761             fheroes2::Copy( font[246 - 32], 7, 4, font[246 - 32], 9, 5, 1, 1 );
762             fheroes2::Copy( font[246 - 32], 7, 4, font[246 - 32], 10, 4, 1, 1 );
763             fheroes2::Copy( font[246 - 32], 8, 1, font[246 - 32], 11, 4, 1, 4 );
764             fheroes2::Copy( font[246 - 32], 8, 1, font[246 - 32], 10, 5, 1, 1 );
765             fheroes2::Copy( font[246 - 32], 9, 5, font[246 - 32], 10, 6, 1, 1 );
766             fheroes2::Copy( font[246 - 32], 9, 5, font[246 - 32], 10, 7, 1, 1 );
767             font[246 - 32].setPosition( font[85].x(), font[85].y() );
768             fheroes2::updateShadow( font[246 - 32], { -1, 2 }, 2 );
769 
770             font[247 - 32] = font[85];
771             fheroes2::Copy( font[247 - 32], 2, 5, font[247 - 32], 2, 3, 6, 2 );
772             fheroes2::Copy( font[247 - 32], 8, 0, font[247 - 32], 7, 4, 1, 1 );
773             fheroes2::Copy( font[247 - 32], 8, 0, font[247 - 32], 7, 5, 1, 1 );
774             fheroes2::Copy( font[247 - 32], 8, 0, font[247 - 32], 7, 6, 1, 1 );
775             fheroes2::FillTransform( font[247 - 32], 1, 5, 6, 4, 1 );
776             fheroes2::updateShadow( font[247 - 32], { -1, 2 }, 2 );
777 
778             font[248 - 32].resize( font[85].width() + 3, font[85].height() );
779             font[248 - 32].reset();
780             fheroes2::Copy( font[85], 0, 0, font[248 - 32], 0, 0, 4, 7 );
781             fheroes2::Copy( font[85], 1, 0, font[248 - 32], 5, 0, 4, 7 );
782             fheroes2::Copy( font[85], 6, 0, font[248 - 32], 9, 0, 4, 7 );
783             fheroes2::Copy( font[248 - 32], 8, 5, font[248 - 32], 4, 5, 4, 2 );
784             font[248 - 32].setPosition( font[85].x(), font[85].y() );
785             fheroes2::updateShadow( font[248 - 32], { -1, 2 }, 2 );
786 
787             font[249 - 32].resize( font[248 - 32].width() + 2, font[248 - 32].height() );
788             font[249 - 32].reset();
789             fheroes2::Copy( font[248 - 32], 0, 0, font[249 - 32], 0, 0, 12, 7 );
790             fheroes2::Copy( font[246 - 32], 9, 4, font[249 - 32], 12, 4, 3, 4 );
791             font[249 - 32].setPosition( font[248 - 32].x(), font[248 - 32].y() );
792             fheroes2::updateShadow( font[249 - 32], { -1, 2 }, 2 );
793 
794             font[252 - 32] = font[226 - 32];
795             fheroes2::FillTransform( font[252 - 32], 4, 0, 5, 3, 1 );
796 
797             font[250 - 32].resize( font[252 - 32].width() + 1, font[252 - 32].height() );
798             font[250 - 32].reset();
799             fheroes2::Copy( font[252 - 32], 0, 0, font[250 - 32], 1, 0, font[252 - 32].width(), font[252 - 32].height() );
800             fheroes2::Copy( font[252 - 32], 1, 0, font[250 - 32], 1, 0, 1, 2 );
801             font[250 - 32].setPosition( font[252 - 32].x(), font[252 - 32].y() );
802             fheroes2::updateShadow( font[250 - 32], { -1, 2 }, 2 );
803 
804             font[251 - 32].resize( font[252 - 32].width() + 3, font[252 - 32].height() );
805             font[251 - 32].reset();
806             fheroes2::Copy( font[252 - 32], 0, 0, font[251 - 32], 0, 0, font[252 - 32].width(), font[252 - 32].height() );
807             fheroes2::Copy( font[252 - 32], 2, 0, font[251 - 32], 10, 0, 2, 7 );
808             font[251 - 32].setPosition( font[252 - 32].x(), font[252 - 32].y() );
809             fheroes2::updateShadow( font[251 - 32], { -1, 2 }, 2 );
810 
811             font[253 - 32] = font[79];
812             fheroes2::FillTransform( font[253 - 32], 0, 2, 3, 3, 1 );
813             fheroes2::Copy( font[253 - 32], 8, 3, font[253 - 32], 7, 3, 1, 1 );
814             fheroes2::Copy( font[253 - 32], 8, 3, font[253 - 32], 6, 3, 1, 1 );
815             fheroes2::Copy( font[253 - 32], 8, 3, font[253 - 32], 5, 3, 1, 1 );
816             fheroes2::updateShadow( font[253 - 32], { -1, 2 }, 2 );
817 
818             font[254 - 32].resize( font[79].width() + 1, font[79].height() );
819             font[254 - 32].reset();
820             fheroes2::Copy( font[251 - 32], 1, 0, font[254 - 32], 1, 0, 3, 7 );
821             fheroes2::Copy( font[79], 2, 1, font[254 - 32], 6, 1, 1, 5 );
822             fheroes2::Copy( font[79], 3, 0, font[254 - 32], 6, 0, 3, 2 );
823             fheroes2::Copy( font[79], 7, 0, font[254 - 32], 9, 0, 1, 2 );
824             fheroes2::Copy( font[79], 8, 2, font[254 - 32], 9, 2, 1, 3 );
825             fheroes2::Copy( font[79], 7, 5, font[254 - 32], 9, 5, 1, 2 );
826             fheroes2::Copy( font[79], 3, 6, font[254 - 32], 6, 6, 3, 1 );
827             fheroes2::Copy( font[254 - 32], 1, 0, font[254 - 32], 4, 3, 2, 1 );
828             font[254 - 32].setPosition( font[251 - 32].x(), font[251 - 32].y() );
829             fheroes2::updateShadow( font[254 - 32], { -1, 2 }, 2 );
830 
831             font[255 - 32] = font[65];
832             fheroes2::FillTransform( font[255 - 32], 0, 2, 6, 3, 1 );
833             fheroes2::Copy( font[69], 2, 5, font[255 - 32], 1, 2, 6, 2 );
834             fheroes2::Copy( font[255 - 32], 6, 4, font[255 - 32], 6, 3, 1, 1 );
835             fheroes2::updateShadow( font[255 - 32], { -1, 2 }, 2 );
836         }
837 
838         // Small font.
839         {
840             std::vector<fheroes2::Sprite> & font = _icnVsSprite[ICN::SMALFONT];
841 
842             size_t offset = 0;
843 
844             font[168 - 32] = font[37 + offset];
845 
846             font[192 - 32] = font[33 + offset];
847 
848             font[193 - 32] = font[34 + offset];
849             fheroes2::FillTransform( font[193 - 32], 5, 1, 2, 2, 1 );
850             fheroes2::Copy( font[193 - 32], 5, 0, font[193 - 32], 6, 0, 1, 1 );
851             fheroes2::updateShadow( font[193 - 32], { -1, 1 }, 2 );
852 
853             font[194 - 32] = font[34 + offset];
854 
855             font[195 - 32].resize( font[193 - 32].width() + 1, font[193 - 32].height() );
856             font[195 - 32].reset();
857             fheroes2::Copy( font[193 - 32], 0, 0, font[195 - 32], 0, 0, 4, 8 );
858             fheroes2::Copy( font[193 - 32], 3, 0, font[195 - 32], 4, 0, 4, 1 );
859             font[195 - 32].setPosition( font[193 - 32].x(), font[193 - 32].y() );
860             fheroes2::updateShadow( font[195 - 32], { -1, 1 }, 2 );
861 
862             font[196 - 32] = font[36 + offset];
863             font[197 - 32] = font[37 + offset];
864 
865             font[198 - 32].resize( font[56].width() + 1, font[56].height() );
866             font[198 - 32].reset();
867             fheroes2::Copy( font[56], 1, 0, font[198 - 32], 1, 0, 3, 7 );
868             fheroes2::Copy( font[56], 7, 0, font[198 - 32], 7, 0, 2, 7 );
869             fheroes2::Copy( font[56], 4, 2, font[198 - 32], 3, 2, 1, 3 );
870             fheroes2::Copy( font[56], 6, 2, font[198 - 32], 7, 2, 1, 3 );
871             fheroes2::Copy( font[37], 4, 5, font[198 - 32], 4, 2, 3, 3 );
872             fheroes2::Copy( font[37], 3, 0, font[198 - 32], 5, 0, 1, 7 );
873             fheroes2::Copy( font[56], 8, 0, font[198 - 32], 9, 0, 1, 7 );
874             font[198 - 32].setPosition( font[56].x(), font[56].y() );
875             fheroes2::updateShadow( font[198 - 32], { -1, 1 }, 2 );
876 
877             font[199 - 32].resize( font[19].width() + 2, font[19].height() );
878             font[199 - 32].reset();
879             fheroes2::Copy( font[19], 1, 0, font[199 - 32], 1, 0, 3, 2 );
880             fheroes2::Copy( font[19], 2, 0, font[199 - 32], 4, 0, 3, 2 );
881             fheroes2::Copy( font[19], 2, 2, font[199 - 32], 3, 2, 3, 3 );
882             fheroes2::Copy( font[19], 2, 5, font[199 - 32], 4, 5, 3, 2 );
883             fheroes2::Copy( font[19], 1, 5, font[199 - 32], 1, 5, 3, 2 );
884             fheroes2::FillTransform( font[199 - 32], 2, 4, 3, 2, 1 );
885             fheroes2::FillTransform( font[199 - 32], 5, 5, 1, 1, 1 );
886             fheroes2::FillTransform( font[199 - 32], 4, 2, 1, 1, 1 );
887             font[199 - 32].setPosition( font[19].x(), font[19].y() );
888             fheroes2::updateShadow( font[199 - 32], { -1, 1 }, 2 );
889 
890             font[200 - 32] = font[40];
891             fheroes2::FillTransform( font[200 - 32], 4, 2, 3, 4, 1 );
892             fheroes2::Copy( font[40], 3, 0, font[200 - 32], 4, 4, 1, 1 );
893             fheroes2::Copy( font[40], 3, 0, font[200 - 32], 5, 3, 1, 1 );
894             fheroes2::Copy( font[40], 3, 0, font[200 - 32], 6, 2, 1, 1 );
895             fheroes2::updateShadow( font[200 - 32], { -1, 1 }, 2 );
896 
897             font[201 - 32].resize( font[200 - 32].width(), font[200 - 32].height() + 2 );
898             font[201 - 32].reset();
899             fheroes2::Copy( font[200 - 32], 1, 0, font[201 - 32], 1, 2, 8, 7 );
900             fheroes2::Copy( font[200 - 32], 2, 0, font[201 - 32], 5, 0, 2, 1 );
901             font[201 - 32].setPosition( font[200 - 32].x(), font[200 - 32].y() - 2 );
902             fheroes2::updateShadow( font[201 - 32], { -1, 1 }, 2 );
903 
904             font[202 - 32] = font[43 + offset];
905 
906             font[203 - 32].resize( font[34].width(), font[34].height() );
907             font[203 - 32].reset();
908             fheroes2::Copy( font[34], 1, 0, font[203 - 32], 1, 0, 3, 7 );
909             fheroes2::Copy( font[34], 3, 0, font[203 - 32], 6, 0, 1, 7 );
910             fheroes2::Copy( font[34], 3, 0, font[203 - 32], 4, 0, 2, 1 );
911             fheroes2::FillTransform( font[203 - 32], 1, 0, 2, 2, 1 );
912             fheroes2::FillTransform( font[203 - 32], 3, 0, 1, 1, 1 );
913             font[203 - 32].setPosition( font[34].x(), font[34].y() );
914             fheroes2::updateShadow( font[203 - 32], { -1, 1 }, 2 );
915 
916             font[204 - 32] = font[45 + offset];
917             font[205 - 32] = font[40 + offset];
918             font[206 - 32] = font[47 + offset];
919 
920             font[207 - 32] = font[195 - 32];
921             fheroes2::Copy( font[207 - 32], 3, 0, font[207 - 32], 6, 0, 1, 7 );
922             fheroes2::updateShadow( font[207 - 32], { -1, 1 }, 2 );
923 
924             font[208 - 32] = font[48 + offset];
925             font[209 - 32] = font[35 + offset];
926 
927             font[210 - 32].resize( font[207 - 32].width() + 2, font[207 - 32].height() );
928             font[210 - 32].reset();
929             fheroes2::Copy( font[207 - 32], 0, 0, font[210 - 32], 0, 0, font[207 - 32].width(), font[207 - 32].height() );
930             fheroes2::Copy( font[210 - 32], 5, 0, font[210 - 32], 8, 0, 2, 8 );
931             font[210 - 32].setPosition( font[207 - 32].x(), font[207 - 32].y() );
932 
933             font[211 - 32] = font[57 + offset];
934             font[213 - 32] = font[56 + offset];
935 
936             font[214 - 32].resize( font[53].width(), font[53].height() + 1 );
937             font[214 - 32].reset();
938             fheroes2::Copy( font[53], 1, 0, font[214 - 32], 1, 0, 8, 7 );
939             fheroes2::Copy( font[214 - 32], 3, 0, font[214 - 32], 9, 5, 1, 3 );
940             font[214 - 32].setPosition( font[53].x(), font[53].y() );
941             fheroes2::updateShadow( font[214 - 32], { -1, 1 }, 2 );
942 
943             font[215 - 32] = font[53];
944             fheroes2::Copy( font[53], 3, 5, font[215 - 32], 3, 2, 4, 2 );
945             fheroes2::FillTransform( font[215 - 32], 2, 4, 5, 4, 1 );
946             fheroes2::updateShadow( font[215 - 32], { -1, 1 }, 2 );
947 
948             font[216 - 32].resize( font[53].width(), font[53].height() );
949             font[216 - 32].reset();
950             fheroes2::Copy( font[53], 1, 0, font[216 - 32], 1, 0, 4, 7 );
951             fheroes2::Copy( font[53], 7, 1, font[216 - 32], 6, 1, 2, 6 );
952             fheroes2::Copy( font[44], 3, 0, font[216 - 32], 9, 0, 1, 8 );
953             fheroes2::Copy( font[53], 7, 1, font[216 - 32], 5, 5, 1, 1 );
954             fheroes2::Copy( font[53], 7, 1, font[216 - 32], 8, 5, 1, 1 );
955             fheroes2::Copy( font[53], 7, 1, font[216 - 32], 6, 0, 1, 1 );
956             font[216 - 32].setPosition( font[53].x(), font[53].y() );
957             fheroes2::updateShadow( font[216 - 32], { -1, 1 }, 2 );
958 
959             font[217 - 32].resize( font[216 - 32].width() + 2, font[216 - 32].height() + 1 );
960             font[217 - 32].reset();
961             fheroes2::Copy( font[216 - 32], 1, 0, font[217 - 32], 1, 0, 9, 7 );
962             fheroes2::Copy( font[216 - 32], 3, 0, font[217 - 32], 10, 6, 1, 1 );
963             fheroes2::Copy( font[216 - 32], 3, 0, font[217 - 32], 11, 5, 1, 3 );
964             font[217 - 32].setPosition( font[216 - 32].x(), font[216 - 32].y() );
965             fheroes2::updateShadow( font[217 - 32], { -1, 1 }, 2 );
966 
967             font[220 - 32].resize( font[34].width(), font[34].height() );
968             font[220 - 32].reset();
969             fheroes2::Copy( font[34], 2, 0, font[220 - 32], 1, 0, 2, 7 );
970             fheroes2::Copy( font[34], 4, 3, font[220 - 32], 3, 3, 1, 4 );
971             fheroes2::Copy( font[34], 4, 3, font[220 - 32], 4, 3, 3, 4 );
972             fheroes2::FillTransform( font[220 - 32], 1, 0, 1, 1, 1 );
973             font[220 - 32].setPosition( font[34].x(), font[34].y() );
974             fheroes2::updateShadow( font[220 - 32], { -1, 1 }, 2 );
975 
976             font[219 - 32].resize( font[220 - 32].width() + 2, font[220 - 32].height() );
977             font[219 - 32].reset();
978             fheroes2::Copy( font[220 - 32], 1, 0, font[219 - 32], 1, 0, 6, 7 );
979             fheroes2::Copy( font[220 - 32], 2, 0, font[219 - 32], 8, 0, 1, 7 );
980             font[219 - 32].setPosition( font[220 - 32].x(), font[220 - 32].y() );
981             fheroes2::updateShadow( font[219 - 32], { -1, 1 }, 2 );
982 
983             font[218 - 32].resize( font[220 - 32].width() + 2, font[220 - 32].height() );
984             font[218 - 32].reset();
985             fheroes2::Copy( font[220 - 32], 1, 0, font[218 - 32], 3, 0, 6, 7 );
986             fheroes2::Copy( font[220 - 32], 2, 3, font[218 - 32], 1, 0, 3, 1 );
987             fheroes2::Copy( font[220 - 32], 2, 3, font[218 - 32], 1, 1, 1, 1 );
988             font[218 - 32].setPosition( font[220 - 32].x(), font[220 - 32].y() );
989             fheroes2::updateShadow( font[218 - 32], { -1, 1 }, 2 );
990 
991             font[221 - 32].resize( font[47].width() - 1, font[47].height() );
992             font[221 - 32].reset();
993             fheroes2::Copy( font[47], 2, 0, font[221 - 32], 1, 0, 6, 7 );
994             fheroes2::Copy( font[47], 3, 0, font[221 - 32], 4, 3, 2, 1 );
995             font[221 - 32].setPosition( font[47].x(), font[47].y() );
996             fheroes2::updateShadow( font[221 - 32], { -1, 1 }, 2 );
997 
998             font[222 - 32].resize( font[47].width() + 1, font[47].height() );
999             font[222 - 32].reset();
1000             fheroes2::Copy( font[44], 2, 0, font[222 - 32], 1, 0, 2, 7 );
1001             fheroes2::Copy( font[47], 2, 0, font[222 - 32], 4, 0, 5, 2 );
1002             fheroes2::Copy( font[47], 2, 5, font[222 - 32], 4, 5, 5, 2 );
1003             fheroes2::Copy( font[222 - 32], 1, 0, font[222 - 32], 3, 3, 1, 1 );
1004             fheroes2::Copy( font[222 - 32], 2, 0, font[222 - 32], 4, 2, 1, 3 );
1005             fheroes2::Copy( font[222 - 32], 2, 0, font[222 - 32], 8, 2, 1, 3 );
1006             font[222 - 32].setPosition( font[47].x(), font[47].y() );
1007             fheroes2::updateShadow( font[222 - 32], { -1, 1 }, 2 );
1008 
1009             font[223 - 32].resize( font[48].width() - 1, font[48].height() );
1010             font[223 - 32].reset();
1011             fheroes2::Copy( font[48], 7, 1, font[223 - 32], 2, 1, 1, 2 );
1012             fheroes2::Copy( font[48], 2, 0, font[223 - 32], 3, 0, 3, 1 );
1013             fheroes2::Copy( font[48], 2, 0, font[223 - 32], 3, 3, 3, 1 );
1014             fheroes2::Copy( font[44], 3, 0, font[223 - 32], 6, 0, 1, 7 );
1015             fheroes2::Copy( font[33], 1, 6, font[223 - 32], 1, 6, 2, 1 );
1016             fheroes2::Copy( font[33], 1, 6, font[223 - 32], 3, 5, 1, 1 );
1017             fheroes2::Copy( font[33], 1, 6, font[223 - 32], 4, 4, 1, 1 );
1018             font[223 - 32].setPosition( font[48].x(), font[48].y() );
1019             fheroes2::updateShadow( font[223 - 32], { -1, 1 }, 2 );
1020 
1021             offset = 32;
1022 
1023             font[184 - 32] = font[37 + offset];
1024 
1025             font[224 - 32] = font[33 + offset];
1026 
1027             font[225 - 32].resize( font[34].width(), font[34].height() );
1028             font[225 - 32].reset();
1029             fheroes2::Copy( font[34], 4, 3, font[225 - 32], 4, 3, 3, 4 );
1030             fheroes2::Copy( font[65], 1, 2, font[225 - 32], 2, 4, 2, 3 );
1031             fheroes2::FillTransform( font[225 - 32], 3, 5, 1, 1, 1 );
1032             fheroes2::Copy( font[225 - 32], 2, 5, font[225 - 32], 2, 1, 2, 2 );
1033             fheroes2::Copy( font[37], 2, 0, font[225 - 32], 2, 0, 5, 1 );
1034             font[225 - 32].setPosition( font[34].x(), font[34].y() );
1035             fheroes2::updateShadow( font[225 - 32], { -1, 1 }, 2 );
1036 
1037             font[226 - 32].resize( font[82].width() - 1, font[82].height() );
1038             font[226 - 32].reset();
1039             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 1, 0, 2, 5 );
1040             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 3, 0, 2, 1 );
1041             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 3, 2, 2, 1 );
1042             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 3, 4, 2, 1 );
1043             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 5, 1, 1, 1 );
1044             fheroes2::Copy( font[82], 1, 0, font[226 - 32], 5, 3, 1, 1 );
1045             font[226 - 32].setPosition( font[82].x(), font[82].y() );
1046             fheroes2::updateShadow( font[226 - 32], { -1, 1 }, 2 );
1047 
1048             font[227 - 32] = font[82];
1049             fheroes2::Copy( font[227 - 32], 3, 1, font[227 - 32], 3, 0, 1, 1 );
1050             fheroes2::FillTransform( font[227 - 32], 3, 1, 1, 1, 1 );
1051             fheroes2::updateShadow( font[227 - 32], { -1, 1 }, 2 );
1052 
1053             font[228 - 32] = font[71];
1054 
1055             font[229 - 32] = font[37 + offset];
1056 
1057             font[230 - 32].resize( font[88].width() + 1, font[88].height() );
1058             font[230 - 32].reset();
1059             fheroes2::Copy( font[88], 0, 0, font[230 - 32], 0, 0, 4, 5 );
1060             fheroes2::Copy( font[88], 4, 0, font[230 - 32], 5, 0, 4, 5 );
1061             fheroes2::Copy( font[85], 2, 0, font[230 - 32], 4, 0, 1, 4 );
1062             fheroes2::Copy( font[85], 2, 0, font[230 - 32], 4, 4, 1, 1 );
1063             font[230 - 32].setPosition( font[88].x(), font[88].y() );
1064             fheroes2::updateShadow( font[230 - 32], { -1, 1 }, 2 );
1065 
1066             font[232 - 32] = font[85];
1067 
1068             font[233 - 32].resize( font[232 - 32].width(), font[232 - 32].height() + 2 );
1069             font[233 - 32].reset();
1070             fheroes2::Copy( font[232 - 32], 1, 0, font[233 - 32], 1, 2, 7, 5 );
1071             fheroes2::Copy( font[232 - 32], 1, 0, font[233 - 32], 3, 0, 2, 1 );
1072             font[233 - 32].setPosition( font[232 - 32].x(), font[232 - 32].y() - 2 );
1073             fheroes2::updateShadow( font[233 - 32], { -1, 1 }, 2 );
1074 
1075             font[234 - 32].resize( font[75].width() - 1, font[75].height() - 2 );
1076             font[234 - 32].reset();
1077             fheroes2::Copy( font[75], 1, 0, font[234 - 32], 1, 0, 2, 5 );
1078             fheroes2::Copy( font[75], 4, 1, font[234 - 32], 3, 0, 3, 3 );
1079             fheroes2::Copy( font[75], 5, 4, font[234 - 32], 4, 3, 2, 2 );
1080             font[234 - 32].setPosition( font[75].x(), font[75].y() + 2 );
1081             fheroes2::updateShadow( font[234 - 32], { -1, 1 }, 2 );
1082 
1083             font[235 - 32].resize( font[65].width() - 2, font[65].height() );
1084             font[235 - 32].reset();
1085             fheroes2::Copy( font[203 - 32], 2, 3, font[235 - 32], 1, 1, 2, 4 );
1086             fheroes2::Copy( font[203 - 32], 5, 0, font[235 - 32], 3, 0, 2, 5 );
1087             fheroes2::FillTransform( font[235 - 32], 1, 1, 1, 1, 1 );
1088             font[235 - 32].setPosition( font[65].x(), font[65].y() );
1089             fheroes2::updateShadow( font[235 - 32], { -1, 1 }, 2 );
1090 
1091             font[236 - 32].resize( font[235 - 32].width() + 3, font[235 - 32].height() );
1092             font[236 - 32].reset();
1093             fheroes2::Copy( font[235 - 32], 4, 0, font[236 - 32], 4, 0, 1, 5 );
1094             fheroes2::Copy( font[235 - 32], 1, 0, font[236 - 32], 1, 1, 3, 3 );
1095             fheroes2::Copy( font[236 - 32], 2, 0, font[236 - 32], 5, 0, 3, 5 );
1096             fheroes2::Copy( font[236 - 32], 4, 0, font[236 - 32], 1, 4, 1, 1 );
1097             font[236 - 32].setPosition( font[235 - 32].x(), font[235 - 32].y() );
1098             fheroes2::updateShadow( font[236 - 32], { -1, 1 }, 2 );
1099 
1100             font[237 - 32].resize( font[72].width() - 2, font[72].height() - 2 );
1101             font[237 - 32].reset();
1102             fheroes2::Copy( font[72], 1, 0, font[237 - 32], 1, 0, 2, 5 );
1103             fheroes2::Copy( font[72], 2, 0, font[237 - 32], 5, 0, 1, 5 );
1104             fheroes2::Copy( font[72], 3, 2, font[237 - 32], 3, 2, 2, 1 );
1105             font[237 - 32].setPosition( font[72].x(), font[72].y() + 2 );
1106             fheroes2::updateShadow( font[237 - 32], { -1, 1 }, 2 );
1107 
1108             font[238 - 32] = font[47 + offset];
1109             font[239 - 32] = font[78];
1110             font[240 - 32] = font[48 + offset];
1111             font[241 - 32] = font[35 + offset];
1112 
1113             font[242 - 32].resize( font[77].width() - 4, font[77].height() );
1114             font[242 - 32].reset();
1115             fheroes2::Copy( font[77], 1, 0, font[242 - 32], 1, 0, 2, 5 );
1116             fheroes2::Copy( font[77], 6, 1, font[242 - 32], 4, 1, 1, 4 );
1117             fheroes2::Copy( font[77], 10, 1, font[242 - 32], 6, 1, 2, 4 );
1118             fheroes2::Copy( font[77], 3, 0, font[242 - 32], 2, 0, 3, 1 );
1119             fheroes2::Copy( font[77], 3, 0, font[242 - 32], 5, 0, 1, 1 );
1120             font[242 - 32].setPosition( font[77].x(), font[77].y() );
1121             fheroes2::updateShadow( font[242 - 32], { -1, 1 }, 2 );
1122 
1123             font[243 - 32] = font[57 + offset];
1124 
1125             font[244 - 32].resize( font[81].width() + 3, font[81].height() + 1 );
1126             font[244 - 32].reset();
1127             fheroes2::Copy( font[81], 1, 0, font[244 - 32], 1, 0, 5, 7 );
1128             fheroes2::Copy( font[81], 2, 0, font[244 - 32], 6, 0, 4, 4 );
1129             fheroes2::Copy( font[81], 2, 4, font[244 - 32], 6, 4, 3, 1 );
1130             fheroes2::Copy( font[81], 2, 4, font[244 - 32], 5, 7, 1, 1 );
1131             font[244 - 32].setPosition( font[81].x(), font[81].y() );
1132             fheroes2::updateShadow( font[244 - 32], { -1, 1 }, 2 );
1133 
1134             // Bigger letter
1135             font[212 - 32] = font[244 - 32];
1136             fheroes2::Copy( font[212 - 32], 5, 1, font[212 - 32], 5, 0, 1, 1 );
1137             fheroes2::Copy( font[212 - 32], 5, 1, font[212 - 32], 4, 7, 1, 1 );
1138             font[212 - 32].setPosition( font[48].x(), font[48].y() ); // copy from a big better
1139             fheroes2::updateShadow( font[212 - 32], { -1, 1 }, 2 );
1140 
1141             font[245 - 32] = font[56 + offset];
1142 
1143             font[246 - 32].resize( font[85].width() + 1, font[85].height() + 1 );
1144             font[246 - 32].reset();
1145             fheroes2::Copy( font[85], 0, 0, font[246 - 32], 0, 0, font[85].width(), font[85].height() );
1146             fheroes2::Copy( font[246 - 32], 2, 0, font[246 - 32], 8, 3, 1, 3 );
1147             font[246 - 32].setPosition( font[85].x(), font[85].y() );
1148             fheroes2::updateShadow( font[246 - 32], { -1, 1 }, 2 );
1149 
1150             font[247 - 32] = font[85];
1151             fheroes2::Copy( font[85], 2, 4, font[247 - 32], 2, 2, 4, 1 );
1152             fheroes2::Copy( font[85], 1, 0, font[247 - 32], 6, 4, 1, 1 );
1153             fheroes2::FillTransform( font[247 - 32], 1, 3, 5, 3, 1 );
1154             fheroes2::updateShadow( font[247 - 32], { -1, 1 }, 2 );
1155 
1156             font[248 - 32].resize( font[85].width() + 2, font[85].height() );
1157             font[248 - 32].reset();
1158             fheroes2::Copy( font[85], 1, 0, font[248 - 32], 1, 0, 3, 5 );
1159             fheroes2::Copy( font[85], 6, 0, font[248 - 32], 5, 0, 2, 5 );
1160             fheroes2::Copy( font[85], 6, 0, font[248 - 32], 8, 0, 2, 5 );
1161             fheroes2::Copy( font[85], 1, 0, font[248 - 32], 4, 4, 1, 1 );
1162             fheroes2::Copy( font[85], 1, 0, font[248 - 32], 7, 4, 1, 1 );
1163             font[248 - 32].setPosition( font[85].x(), font[85].y() );
1164             fheroes2::updateShadow( font[248 - 32], { -1, 1 }, 2 );
1165 
1166             font[249 - 32].resize( font[248 - 32].width() + 1, font[248 - 32].height() );
1167             font[249 - 32].reset();
1168             fheroes2::Copy( font[248 - 32], 1, 0, font[249 - 32], 1, 0, 9, 5 );
1169             fheroes2::Copy( font[248 - 32], 2, 0, font[249 - 32], 10, 3, 1, 3 );
1170             font[249 - 32].setPosition( font[248 - 32].x(), font[248 - 32].y() );
1171             fheroes2::updateShadow( font[249 - 32], { -1, 1 }, 2 );
1172 
1173             font[252 - 32] = font[226 - 32];
1174             fheroes2::Copy( font[252 - 32], 1, 0, font[252 - 32], 5, 4, 1, 1 );
1175             fheroes2::FillTransform( font[252 - 32], 0, 0, 2, 2, 1 );
1176             fheroes2::FillTransform( font[252 - 32], 3, 0, 3, 2, 1 );
1177 
1178             font[250 - 32].resize( font[252 - 32].width() + 1, font[252 - 32].height() );
1179             font[250 - 32].reset();
1180             fheroes2::Copy( font[252 - 32], 1, 0, font[250 - 32], 2, 0, 5, 5 );
1181             fheroes2::Copy( font[252 - 32], 2, 2, font[250 - 32], 1, 0, 2, 1 );
1182             fheroes2::Copy( font[252 - 32], 2, 2, font[250 - 32], 1, 1, 1, 1 );
1183             font[250 - 32].setPosition( font[252 - 32].x(), font[252 - 32].y() );
1184             fheroes2::updateShadow( font[250 - 32], { -1, 1 }, 2 );
1185 
1186             font[251 - 32].resize( font[252 - 32].width() + 2, font[252 - 32].height() );
1187             font[251 - 32].reset();
1188             fheroes2::Copy( font[252 - 32], 1, 0, font[251 - 32], 1, 0, 5, 5 );
1189             fheroes2::Copy( font[252 - 32], 2, 0, font[251 - 32], 7, 0, 1, 5 );
1190             font[251 - 32].setPosition( font[252 - 32].x(), font[252 - 32].y() );
1191             fheroes2::updateShadow( font[251 - 32], { -1, 1 }, 2 );
1192 
1193             font[253 - 32].resize( font[79].width() - 1, font[79].height() );
1194             font[253 - 32].reset();
1195             fheroes2::Copy( font[79], 2, 0, font[253 - 32], 1, 0, 4, 5 );
1196             fheroes2::Copy( font[79], 2, 0, font[253 - 32], 2, 2, 2, 1 );
1197             font[253 - 32].setPosition( font[79].x(), font[79].y() );
1198             fheroes2::updateShadow( font[253 - 32], { -1, 1 }, 2 );
1199 
1200             font[231 - 32] = font[253 - 32];
1201             fheroes2::FillTransform( font[231 - 32], 0, 1, 3, 3, 1 );
1202             fheroes2::FillTransform( font[231 - 32], 4, 2, 1, 1, 1 );
1203             fheroes2::FillTransform( font[231 - 32], 1, 0, 1, 1, 1 );
1204             fheroes2::Copy( font[253 - 32], 1, 0, font[231 - 32], 1, 1, 1, 1 );
1205             fheroes2::updateShadow( font[231 - 32], { -1, 1 }, 2 );
1206 
1207             font[254 - 32].resize( font[79].width() + 2, font[79].height() );
1208             font[254 - 32].reset();
1209             fheroes2::Copy( font[82], 1, 0, font[254 - 32], 1, 0, 2, 5 );
1210             fheroes2::Copy( font[79], 1, 0, font[254 - 32], 4, 0, 3, 5 );
1211             fheroes2::Copy( font[79], 5, 1, font[254 - 32], 7, 1, 1, 3 );
1212             fheroes2::Copy( font[79], 5, 1, font[254 - 32], 3, 2, 1, 1 );
1213             font[254 - 32].setPosition( font[79].x(), font[79].y() );
1214             fheroes2::updateShadow( font[254 - 32], { -1, 1 }, 2 );
1215 
1216             font[255 - 32].resize( font[65].width() - 1, font[65].height() );
1217             font[255 - 32].reset();
1218             fheroes2::Copy( font[65], 1, 2, font[255 - 32], 2, 0, 3, 3 );
1219             fheroes2::Copy( font[235 - 32], 4, 0, font[255 - 32], 5, 0, 1, 5 );
1220             fheroes2::Copy( font[33], 1, 5, font[255 - 32], 1, 3, 3, 2 );
1221             font[255 - 32].setPosition( font[65].x(), font[65].y() );
1222             fheroes2::updateShadow( font[255 - 32], { -1, 1 }, 2 );
1223         }
1224     }
1225 
generateAlphabet(const fheroes2::SupportedLanguage language)1226     void generateAlphabet( const fheroes2::SupportedLanguage language )
1227     {
1228         switch ( language ) {
1229         case fheroes2::SupportedLanguage::Polish:
1230             generatePolishAlphabet();
1231             break;
1232         case fheroes2::SupportedLanguage::German:
1233             generateGermanAlphabet();
1234             break;
1235         case fheroes2::SupportedLanguage::French:
1236             generateFrenchAlphabet();
1237             break;
1238         case fheroes2::SupportedLanguage::Russian:
1239             generateRussianAlphabet();
1240             break;
1241         default:
1242             // Add new language generation code!
1243             assert( 0 );
1244             break;
1245         }
1246 
1247         _icnVsSprite[ICN::YELLOW_FONT].clear();
1248         _icnVsSprite[ICN::YELLOW_SMALLFONT].clear();
1249         _icnVsSprite[ICN::GRAY_FONT].clear();
1250         _icnVsSprite[ICN::GRAY_SMALL_FONT].clear();
1251         _icnVsSprite[ICN::WHITE_LARGE_FONT].clear();
1252     }
1253 
invertTransparency(fheroes2::Image & image)1254     void invertTransparency( fheroes2::Image & image )
1255     {
1256         if ( image.singleLayer() ) {
1257             assert( 0 );
1258             return;
1259         }
1260 
1261         uint8_t * transform = image.transform();
1262         uint8_t * transformEnd = transform + image.width() * image.height();
1263         for ( ; transform != transformEnd; ++transform ) {
1264             if ( *transform == 0 ) {
1265                 *transform = 1;
1266             }
1267             else if ( *transform == 1 ) {
1268                 *transform = 0;
1269             }
1270             // Other transform values are not relevant for transparency checks.
1271         }
1272     }
1273 }
1274 
1275 namespace fheroes2
1276 {
1277     namespace AGG
1278     {
LoadOriginalICN(int id)1279         void LoadOriginalICN( int id )
1280         {
1281             const std::vector<uint8_t> & body = ::AGG::ReadChunk( ICN::GetString( id ) );
1282 
1283             if ( body.empty() ) {
1284                 return;
1285             }
1286 
1287             StreamBuf imageStream( body );
1288 
1289             const uint32_t count = imageStream.getLE16();
1290             const uint32_t blockSize = imageStream.getLE32();
1291             if ( count == 0 || blockSize == 0 ) {
1292                 return;
1293             }
1294 
1295             _icnVsSprite[id].resize( count );
1296 
1297             for ( uint32_t i = 0; i < count; ++i ) {
1298                 imageStream.seek( headerSize + i * 13 );
1299 
1300                 ICNHeader header1;
1301                 imageStream >> header1;
1302 
1303                 uint32_t sizeData = 0;
1304                 if ( i + 1 != count ) {
1305                     ICNHeader header2;
1306                     imageStream >> header2;
1307                     sizeData = header2.offsetData - header1.offsetData;
1308                 }
1309                 else {
1310                     sizeData = blockSize - header1.offsetData;
1311                 }
1312 
1313                 const uint8_t * data = body.data() + headerSize + header1.offsetData;
1314 
1315                 _icnVsSprite[id][i]
1316                     = decodeICNSprite( data, sizeData, header1.width, header1.height, static_cast<int16_t>( header1.offsetX ), static_cast<int16_t>( header1.offsetY ) );
1317             }
1318         }
1319 
1320         // Helper function for LoadModifiedICN
CopyICNWithPalette(int icnId,int originalIcnId,const PAL::PaletteType paletteType)1321         void CopyICNWithPalette( int icnId, int originalIcnId, const PAL::PaletteType paletteType )
1322         {
1323             assert( icnId != originalIcnId );
1324 
1325             GetICN( originalIcnId, 0 ); // always avoid calling LoadOriginalICN directly
1326 
1327             _icnVsSprite[icnId] = _icnVsSprite[originalIcnId];
1328             const std::vector<uint8_t> & palette = PAL::GetPalette( paletteType );
1329             for ( size_t i = 0; i < _icnVsSprite[icnId].size(); ++i ) {
1330                 ApplyPalette( _icnVsSprite[icnId][i], palette );
1331             }
1332         }
1333 
LoadModifiedICN(int id)1334         bool LoadModifiedICN( int id )
1335         {
1336             switch ( id ) {
1337             case ICN::ROUTERED:
1338                 CopyICNWithPalette( id, ICN::ROUTE, PAL::PaletteType::RED );
1339                 return true;
1340             case ICN::FONT:
1341             case ICN::SMALFONT: {
1342                 LoadOriginalICN( id );
1343                 auto & imageArray = _icnVsSprite[id];
1344                 if ( id == ICN::FONT ) {
1345                     // The original images contain an issue: image layer has value 50 which is '2' in UTF-8. We must correct these (only 3) places
1346                     for ( size_t i = 0; i < imageArray.size(); ++i ) {
1347                         ReplaceColorIdByTransformId( imageArray[i], 50, 2 );
1348                     }
1349                 }
1350 
1351                 // Some checks that we really have CP1251 font
1352                 const int32_t verifiedFontWidth = ( id == ICN::FONT ) ? 19 : 12;
1353                 if ( imageArray.size() == 162 && imageArray[121].width() == verifiedFontWidth ) {
1354                     // Engine expects that letter indexes correspond to charcode - 0x20.
1355                     // In case CP1251 font.icn contains sprites for chars 0x20-0x7F, 0xC0-0xDF, 0xA8, 0xE0-0xFF, 0xB8 (in that order).
1356                     // We rearrange sprites array for corresponding sprite indexes to charcode - 0x20.
1357                     imageArray.insert( imageArray.begin() + 96, 64, imageArray[0] );
1358                     std::swap( imageArray[136], imageArray[192] ); // Move sprites for chars 0xA8
1359                     std::swap( imageArray[152], imageArray[225] ); // and 0xB8 to it's places.
1360                     imageArray.pop_back();
1361                     imageArray.erase( imageArray.begin() + 192 );
1362                 }
1363                 return true;
1364             }
1365             case ICN::YELLOW_FONT:
1366                 CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::YELLOW_TEXT );
1367                 return true;
1368             case ICN::YELLOW_SMALLFONT:
1369                 CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::YELLOW_TEXT );
1370                 return true;
1371             case ICN::GRAY_FONT:
1372                 CopyICNWithPalette( id, ICN::FONT, PAL::PaletteType::GRAY_TEXT );
1373                 return true;
1374             case ICN::GRAY_SMALL_FONT:
1375                 CopyICNWithPalette( id, ICN::SMALFONT, PAL::PaletteType::GRAY_TEXT );
1376                 return true;
1377             case ICN::BTNBATTLEONLY:
1378                 _icnVsSprite[id].resize( 2 );
1379                 for ( uint32_t i = 0; i < static_cast<uint32_t>( _icnVsSprite[id].size() ); ++i ) {
1380                     Sprite & out = _icnVsSprite[id][i];
1381                     out = GetICN( ICN::BTNNEWGM, 6 + i );
1382                     // clean the button
1383                     Image uniform( 83, 23 );
1384                     uniform.fill( ( i == 0 ) ? GetColorId( 216, 184, 152 ) : GetColorId( 184, 136, 96 ) );
1385                     Copy( uniform, 0, 0, out, 28, 18, uniform.width(), uniform.height() );
1386                     // add 'ba'
1387                     Blit( GetICN( ICN::BTNCMPGN, i ), 41 - i, 28, out, 30 - i, 13, 28, 14 );
1388                     // add 'tt'
1389                     Blit( GetICN( ICN::BTNNEWGM, i ), 25 - i, 13, out, 57 - i, 13, 13, 14 );
1390                     Blit( GetICN( ICN::BTNNEWGM, i ), 25 - i, 13, out, 70 - i, 13, 13, 14 );
1391                     // add 'le'
1392                     Blit( GetICN( ICN::BTNNEWGM, 6 + i ), 97 - i, 21, out, 83 - i, 13, 13, 14 );
1393                     Blit( GetICN( ICN::BTNNEWGM, 6 + i ), 86 - i, 21, out, 96 - i, 13, 13, 14 );
1394                     // add 'on'
1395                     Blit( GetICN( ICN::BTNDCCFG, 4 + i ), 44 - i, 21, out, 40 - i, 28, 31, 14 );
1396                     // add 'ly'
1397                     Blit( GetICN( ICN::BTNHOTST, i ), 47 - i, 21, out, 71 - i, 28, 12, 13 );
1398                     Blit( GetICN( ICN::BTNHOTST, i ), 72 - i, 21, out, 84 - i, 28, 13, 13 );
1399                 }
1400                 return true;
1401             case ICN::NON_UNIFORM_GOOD_MIN_BUTTON:
1402                 _icnVsSprite[id].resize( 2 );
1403                 for ( uint32_t i = 0; i < static_cast<uint32_t>( _icnVsSprite[id].size() ); ++i ) {
1404                     Sprite & out = _icnVsSprite[id][i];
1405                     out = GetICN( ICN::RECRUIT, 4 + i );
1406                     // clean the button
1407                     Blit( GetICN( ICN::SYSTEM, 11 + i ), 10, 6 + i, out, 30 - 2 * i, 5 + i, 31, 15 );
1408                     // add 'IN'
1409                     Copy( GetICN( ICN::APANEL, 4 + i ), 23 - i, 22 + i, out, 33 - i, 6 + i, 8, 14 ); // letter 'I'
1410                     Copy( GetICN( ICN::APANEL, 4 + i ), 31 - i, 22 + i, out, 44 - i, 6 + i, 17, 14 ); // letter 'N'
1411                 }
1412                 return true;
1413             case ICN::SPELLS:
1414                 LoadOriginalICN( id );
1415                 _icnVsSprite[id].resize( 66 );
1416                 for ( uint32_t i = 60; i < 66; ++i ) {
1417                     int originalIndex = 0;
1418                     if ( i == 60 ) // Mass Cure
1419                         originalIndex = 6;
1420                     else if ( i == 61 ) // Mass Haste
1421                         originalIndex = 14;
1422                     else if ( i == 62 ) // Mass Slow
1423                         originalIndex = 1;
1424                     else if ( i == 63 ) // Mass Bless
1425                         originalIndex = 7;
1426                     else if ( i == 64 ) // Mass Curse
1427                         originalIndex = 3;
1428                     else if ( i == 65 ) // Mass Shield
1429                         originalIndex = 15;
1430 
1431                     const Sprite & originalImage = _icnVsSprite[id][originalIndex];
1432                     Sprite & image = _icnVsSprite[id][i];
1433 
1434                     image.resize( originalImage.width() + 8, originalImage.height() + 8 );
1435                     image.setPosition( originalImage.x() + 4, originalImage.y() + 4 );
1436                     image.fill( 1 );
1437 
1438                     AlphaBlit( originalImage, image, 0, 0, 128 );
1439                     AlphaBlit( originalImage, image, 4, 4, 192 );
1440                     Blit( originalImage, image, 8, 8 );
1441 
1442                     AddTransparency( image, 1 );
1443                 }
1444                 return true;
1445             case ICN::CSLMARKER:
1446                 _icnVsSprite[id].resize( 3 );
1447                 for ( uint32_t i = 0; i < 3; ++i ) {
1448                     _icnVsSprite[id][i] = GetICN( ICN::LOCATORS, 24 );
1449                     if ( i == 1 ) {
1450                         ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xD6 );
1451                     }
1452                     else if ( i == 2 ) {
1453                         ReplaceColorId( _icnVsSprite[id][i], 0x0A, 0xDE );
1454                     }
1455                 }
1456                 return true;
1457             case ICN::BATTLESKIP:
1458                 _icnVsSprite[id].resize( 2 );
1459                 for ( uint32_t i = 0; i < 2; ++i ) {
1460                     Sprite & out = _icnVsSprite[id][i];
1461                     out = GetICN( ICN::TEXTBAR, 4 + i );
1462 
1463                     // clean the button
1464                     Blit( GetICN( ICN::SYSTEM, 11 + i ), 3, 8, out, 3, 1, 43, 14 );
1465 
1466                     // add 'skip'
1467                     Blit( GetICN( ICN::TEXTBAR, i ), 3, 10, out, 3, 0, 43, 14 );
1468                 }
1469                 return true;
1470             case ICN::BATTLEWAIT:
1471                 _icnVsSprite[id].resize( 2 );
1472                 for ( uint32_t i = 0; i < 2; ++i ) {
1473                     Sprite & out = _icnVsSprite[id][i];
1474                     out = GetICN( ICN::TEXTBAR, 4 + i );
1475 
1476                     // clean the button
1477                     Blit( GetICN( ICN::SYSTEM, 11 + i ), 3, 8, out, 3, 1, 43, 14 );
1478 
1479                     // add 'wait'
1480                     const Sprite wait = Crop( GetICN( ICN::ADVBTNS, 8 + i ), 5, 4, 28, 28 );
1481                     Image resizedWait( wait.width() / 2, wait.height() / 2 );
1482                     Resize( wait, resizedWait );
1483 
1484                     Blit( resizedWait, 0, 0, out, ( out.width() - 14 ) / 2, 0, 14, 14 );
1485                 }
1486                 return true;
1487             case ICN::BUYMAX:
1488                 _icnVsSprite[id].resize( 2 );
1489                 for ( uint32_t i = 0; i < 2; ++i ) {
1490                     Sprite & out = _icnVsSprite[id][i];
1491                     out = GetICN( ICN::WELLXTRA, i );
1492 
1493                     // clean the button
1494                     Blit( GetICN( ICN::SYSTEM, 11 + i ), 10, 6, out, 6, 2, 52, 14 );
1495 
1496                     // add 'max'
1497                     Blit( GetICN( ICN::RECRUIT, 4 + i ), 12, 6, out, 7, 3, 50, 12 );
1498                 }
1499                 return true;
1500             case ICN::BTNGIFT_GOOD:
1501                 _icnVsSprite[id].resize( 2 );
1502                 for ( uint32_t i = 0; i < 2; ++i ) {
1503                     Sprite & out = _icnVsSprite[id][i];
1504                     out = GetICN( ICN::TRADPOST, 17 + i );
1505 
1506                     // clean the button
1507                     Blit( GetICN( ICN::SYSTEM, 11 + i ), 10, 6, out, 6, 4, 72, 15 );
1508 
1509                     // add 'G'
1510                     Blit( GetICN( ICN::CPANEL, i ), 18 - i, 27, out, 20 - i, 4, 15, 15 );
1511 
1512                     // add 'I'
1513                     Blit( GetICN( ICN::APANEL, 4 + i ), 22 - i, 20, out, 36 - i, 4, 9, 15 );
1514 
1515                     // add 'F'
1516                     Blit( GetICN( ICN::APANEL, 4 + i ), 48 - i, 20, out, 46 - i, 4, 13, 15 );
1517 
1518                     // add 'T'
1519                     Blit( GetICN( ICN::CPANEL, 6 + i ), 59 - i, 21, out, 60 - i, 5, 14, 14 );
1520                 }
1521                 return true;
1522             case ICN::BTNGIFT_EVIL:
1523                 _icnVsSprite[id].resize( 2 );
1524                 for ( uint32_t i = 0; i < 2; ++i ) {
1525                     Sprite & out = _icnVsSprite[id][i];
1526                     out = GetICN( ICN::TRADPOSE, 17 + i );
1527 
1528                     // clean the button
1529                     Blit( GetICN( ICN::SYSTEME, 11 + i ), 10, 6, out, 6, 4, 72, 15 );
1530 
1531                     // add 'G'
1532                     Blit( GetICN( ICN::CPANELE, i ), 18 - i, 27, out, 20 - i, 4, 15, 15 );
1533 
1534                     // add 'I'
1535                     Blit( GetICN( ICN::APANELE, 4 + i ), 22 - i, 20, out, 36 - i, 4, 9, 15 );
1536 
1537                     // add 'F'
1538                     Blit( GetICN( ICN::APANELE, 4 + i ), 48 - i, 20, out, 46 - i, 4, 13, 15 );
1539 
1540                     // add 'T'
1541                     Blit( GetICN( ICN::CPANELE, 6 + i ), 59 - i, 21, out, 60 - i, 5, 14, 14 );
1542                 }
1543                 return true;
1544             case ICN::BTNCONFIG:
1545                 _icnVsSprite[id].resize( 2 );
1546                 for ( uint32_t i = 0; i < 2; ++i ) {
1547                     Sprite & out = _icnVsSprite[id][i];
1548                     out = GetICN( ICN::NON_UNIFORM_GOOD_OKAY_BUTTON, i );
1549 
1550                     // add 'config'
1551                     Blit( GetICN( ICN::BTNDCCFG, 4 + i ), 31 - i, 20, out, 10 - i, 4, 77, 16 );
1552                 }
1553                 return true;
1554             case ICN::PHOENIX:
1555                 LoadOriginalICN( id );
1556                 // First sprite has cropped shadow. We copy missing part from another 'almost' identical frame
1557                 if ( _icnVsSprite[id].size() >= 32 ) {
1558                     const Sprite & in = _icnVsSprite[id][32];
1559                     Copy( in, 60, 73, _icnVsSprite[id][1], 60, 73, 14, 13 );
1560                     Copy( in, 56, 72, _icnVsSprite[id][30], 56, 72, 18, 9 );
1561                 }
1562                 return true;
1563             case ICN::MONH0028: // phoenix
1564                 LoadOriginalICN( id );
1565                 if ( _icnVsSprite[id].size() == 1 ) {
1566                     const Sprite & correctFrame = GetICN( ICN::PHOENIX, 32 );
1567                     Copy( correctFrame, 60, 73, _icnVsSprite[id][0], 58, 70, 14, 13 );
1568                 }
1569                 return true;
1570             case ICN::CAVALRYR:
1571                 LoadOriginalICN( id );
1572                 // Sprite 23 has incorrect colors, we need to replace them
1573                 if ( _icnVsSprite[id].size() >= 23 ) {
1574                     Sprite & out = _icnVsSprite[id][23];
1575 
1576                     std::vector<uint8_t> indexes( 256 );
1577                     for ( uint32_t i = 0; i < 256; ++i ) {
1578                         indexes[i] = static_cast<uint8_t>( i );
1579                     }
1580 
1581                     indexes[69] = 187;
1582                     indexes[71] = 195;
1583                     indexes[73] = 188;
1584                     indexes[74] = 190;
1585                     indexes[75] = 193;
1586                     indexes[76] = 191;
1587                     indexes[77] = 195;
1588                     indexes[80] = 195;
1589                     indexes[81] = 196;
1590                     indexes[83] = 196;
1591                     indexes[84] = 197;
1592                     indexes[151] = 197;
1593 
1594                     ApplyPalette( out, indexes );
1595                 }
1596                 return true;
1597             case ICN::TROLLMSL:
1598                 LoadOriginalICN( id );
1599                 if ( _icnVsSprite[id].size() == 1 ) {
1600                     Sprite & out = _icnVsSprite[id][0];
1601                     // The original sprite contains 2 pixels which are empty
1602                     if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) {
1603                         out.transform()[147] = 0;
1604                         out.image()[147] = 22;
1605 
1606                         out.transform()[188] = 0;
1607                         out.image()[188] = 24;
1608                     }
1609                 }
1610                 return true;
1611             case ICN::TROLL2MSL:
1612                 LoadOriginalICN( ICN::TROLLMSL );
1613                 if ( _icnVsSprite[ICN::TROLLMSL].size() == 1 ) {
1614                     _icnVsSprite[id].resize( 1 );
1615 
1616                     Sprite & out = _icnVsSprite[id][0];
1617                     out = _icnVsSprite[ICN::TROLLMSL][0];
1618 
1619                     // The original sprite contains 2 pixels which are empty
1620                     if ( out.width() * out.height() > 188 && out.transform()[147] == 1 && out.transform()[188] == 1 ) {
1621                         out.transform()[147] = 0;
1622                         out.image()[147] = 22;
1623 
1624                         out.transform()[188] = 0;
1625                         out.image()[188] = 24;
1626                     }
1627 
1628                     std::vector<uint8_t> indexes( 256 );
1629                     for ( uint32_t i = 0; i < 256; ++i ) {
1630                         indexes[i] = static_cast<uint8_t>( i );
1631                     }
1632 
1633                     indexes[10] = 152;
1634                     indexes[11] = 153;
1635                     indexes[12] = 154;
1636                     indexes[13] = 155;
1637                     indexes[14] = 155;
1638                     indexes[15] = 156;
1639                     indexes[16] = 157;
1640                     indexes[17] = 158;
1641                     indexes[18] = 159;
1642                     indexes[19] = 160;
1643                     indexes[20] = 160;
1644                     indexes[21] = 161;
1645                     indexes[22] = 162;
1646                     indexes[23] = 163;
1647                     indexes[24] = 164;
1648                     indexes[25] = 165;
1649                     indexes[26] = 166;
1650                     indexes[27] = 166;
1651                     indexes[28] = 167;
1652                     indexes[29] = 168;
1653                     indexes[30] = 169;
1654                     indexes[31] = 170;
1655                     indexes[32] = 171;
1656                     indexes[33] = 172;
1657                     indexes[34] = 172;
1658                     indexes[35] = 173;
1659 
1660                     ApplyPalette( out, indexes );
1661                 }
1662                 return true;
1663             case ICN::LOCATORE:
1664             case ICN::LOCATORS:
1665                 LoadOriginalICN( id );
1666                 if ( _icnVsSprite[id].size() > 15 ) {
1667                     if ( _icnVsSprite[id][12].width() == 47 ) {
1668                         Sprite & out = _icnVsSprite[id][12];
1669                         out = Crop( out, 0, 0, out.width() - 1, out.height() );
1670                     }
1671                     if ( _icnVsSprite[id][15].width() == 47 ) {
1672                         Sprite & out = _icnVsSprite[id][15];
1673                         out = Crop( out, 0, 0, out.width() - 1, out.height() );
1674                     }
1675                 }
1676                 return true;
1677             case ICN::TOWNBKG2:
1678                 LoadOriginalICN( id );
1679                 if ( _icnVsSprite[id].size() == 1 ) {
1680                     Sprite & out = _icnVsSprite[id][0];
1681                     // The pixel pixel of the original sprite has a skip value
1682                     if ( !out.empty() && out.transform()[0] == 1 ) {
1683                         out.transform()[0] = 0;
1684                         out.image()[0] = 10;
1685                     }
1686                 }
1687                 return true;
1688             case ICN::HSICONS:
1689                 LoadOriginalICN( id );
1690                 if ( _icnVsSprite[id].size() > 7 ) {
1691                     Sprite & out = _icnVsSprite[id][7];
1692                     if ( out.width() == 34 && out.height() == 19 ) {
1693                         Sprite temp;
1694                         std::swap( temp, out );
1695 
1696                         out.resize( temp.width() + 1, temp.height() );
1697                         out.reset();
1698                         Copy( temp, 0, 0, out, 1, 0, temp.width(), temp.height() );
1699                         Copy( temp, temp.width() - 1, 10, out, 0, 10, 1, 3 );
1700                     }
1701                 }
1702                 return true;
1703             case ICN::LISTBOX_EVIL:
1704                 CopyICNWithPalette( id, ICN::LISTBOX, PAL::PaletteType::GRAY );
1705                 for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) {
1706                     ApplyPalette( _icnVsSprite[id][i], 2 );
1707                 }
1708                 return true;
1709             case ICN::MONS32:
1710                 LoadOriginalICN( id );
1711 
1712                 if ( _icnVsSprite[id].size() > 4 ) { // Veteran Pikeman
1713                     Sprite & modified = _icnVsSprite[id][4];
1714 
1715                     Sprite temp( modified.width(), modified.height() + 1 );
1716                     temp.reset();
1717                     Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() );
1718                     modified = std::move( temp );
1719                     Fill( modified, 7, 0, 4, 1, 36 );
1720                 }
1721                 if ( _icnVsSprite[id].size() > 6 ) { // Master Swordsman
1722                     Sprite & modified = _icnVsSprite[id][6];
1723 
1724                     Sprite temp( modified.width(), modified.height() + 1 );
1725                     temp.reset();
1726                     Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() );
1727                     modified = std::move( temp );
1728                     Fill( modified, 2, 0, 5, 1, 36 );
1729                 }
1730                 if ( _icnVsSprite[id].size() > 8 ) { // Champion
1731                     Sprite & modified = _icnVsSprite[id][8];
1732 
1733                     Sprite temp( modified.width(), modified.height() + 1 );
1734                     temp.reset();
1735                     Blit( modified, 0, 0, temp, 0, 1, modified.width(), modified.height() );
1736                     modified = std::move( temp );
1737                     Fill( modified, 12, 0, 5, 1, 36 );
1738                 }
1739                 if ( _icnVsSprite[id].size() > 62 ) {
1740                     const Point shadowOffset( -1, 2 );
1741                     for ( size_t i = 0; i < 62; ++i ) {
1742                         Sprite & modified = _icnVsSprite[id][i];
1743                         const Point originalOffset( modified.x(), modified.y() );
1744                         Sprite temp = addShadow( modified, Point( -1, 2 ), 2 );
1745                         temp.setPosition( originalOffset.x - 1, originalOffset.y + 2 );
1746 
1747                         const Rect area = GetActiveROI( temp, 2 );
1748                         if ( area.x > 0 || area.height != temp.height() ) {
1749                             const Point offset( temp.x() - area.x, temp.y() - temp.height() + area.y + area.height );
1750                             modified = Crop( temp, area.x, area.y, area.width, area.height );
1751                             modified.setPosition( offset.x, offset.y );
1752                         }
1753                         else {
1754                             modified = std::move( temp );
1755                         }
1756                     }
1757                 }
1758                 if ( _icnVsSprite[id].size() > 63 && _icnVsSprite[id][63].width() == 19 && _icnVsSprite[id][63].height() == 37 ) { // Air Elemental
1759                     Sprite & modified = _icnVsSprite[id][63];
1760                     modified.image()[19 * 9 + 9] = modified.image()[19 * 5 + 11];
1761                     modified.transform()[19 * 9 + 9] = modified.transform()[19 * 5 + 11];
1762                 }
1763 
1764                 return true;
1765             case ICN::MONSTER_SWITCH_LEFT_ARROW:
1766                 _icnVsSprite[id].resize( 2 );
1767                 for ( uint32_t i = 0; i < 2; ++i ) {
1768                     const Sprite & source = GetICN( ICN::RECRUIT, i );
1769                     Sprite & out = _icnVsSprite[id][i];
1770                     out.resize( source.height(), source.width() );
1771                     Transpose( source, out );
1772                     out = Flip( out, false, true );
1773                     out.setPosition( source.y(), source.x() );
1774                 }
1775                 return true;
1776             case ICN::MONSTER_SWITCH_RIGHT_ARROW:
1777                 _icnVsSprite[id].resize( 2 );
1778                 for ( uint32_t i = 0; i < 2; ++i ) {
1779                     const Sprite & source = GetICN( ICN::RECRUIT, i + 2 );
1780                     Sprite & out = _icnVsSprite[id][i];
1781                     out.resize( source.height(), source.width() );
1782                     Transpose( source, out );
1783                     out = Flip( out, false, true );
1784                     out.setPosition( source.y(), source.x() );
1785                 }
1786                 return true;
1787             case ICN::SURRENDR:
1788             case ICN::SURRENDE:
1789                 LoadOriginalICN( id );
1790                 if ( _icnVsSprite[id].size() >= 4 ) {
1791                     if ( id == ICN::SURRENDR ) {
1792                         // Fix incorrect font color on good ACCEPT button.
1793                         ReplaceColorId( _icnVsSprite[id][0], 28, 56 );
1794                     }
1795                     // Fix pressed buttons background.
1796                     for ( uint32_t i : { 0, 2 } ) {
1797                         Sprite & out = _icnVsSprite[id][i + 1];
1798 
1799                         Sprite tmp( out.width(), out.height() );
1800                         tmp.reset();
1801                         Copy( out, 0, 1, tmp, 1, 0, tmp.width() - 1, tmp.height() - 1 );
1802                         CopyTransformLayer( _icnVsSprite[id][i], tmp );
1803 
1804                         out.reset();
1805                         Copy( tmp, 1, 0, out, 0, 1, tmp.width() - 1, tmp.height() - 1 );
1806                     }
1807                 }
1808                 return true;
1809             case ICN::NON_UNIFORM_GOOD_OKAY_BUTTON:
1810                 _icnVsSprite[id].resize( 2 );
1811                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRG, 4 ), 6, 0, 96, 25 );
1812                 _icnVsSprite[id][0].setPosition( 0, 0 );
1813 
1814                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 5 );
1815                 _icnVsSprite[id][1].setPosition( 0, 0 );
1816 
1817                 // fix transparent corners
1818                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1819                 return true;
1820             case ICN::NON_UNIFORM_GOOD_CANCEL_BUTTON:
1821                 _icnVsSprite[id].resize( 2 );
1822                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRG, 6 ), 6, 0, 96, 25 );
1823                 _icnVsSprite[id][0].setPosition( 0, 0 );
1824 
1825                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 7 );
1826                 _icnVsSprite[id][1].setPosition( 0, 0 );
1827 
1828                 // fix transparent corners
1829                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1830                 return true;
1831             case ICN::NON_UNIFORM_GOOD_RESTART_BUTTON:
1832                 _icnVsSprite[id].resize( 2 );
1833                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRG, 2 ), 6, 0, 108, 25 );
1834                 _icnVsSprite[id][0].setPosition( 0, 0 );
1835 
1836                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRG, 3 );
1837                 _icnVsSprite[id][1].setPosition( 0, 0 );
1838 
1839                 // fix transparent corners
1840                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1841                 return true;
1842             case ICN::NON_UNIFORM_EVIL_OKAY_BUTTON:
1843                 _icnVsSprite[id].resize( 2 );
1844                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRE, 4 ), 4, 0, 96, 25 );
1845                 _icnVsSprite[id][0].setPosition( 0, 0 );
1846 
1847                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 5 );
1848                 _icnVsSprite[id][1].setPosition( 0, 0 );
1849 
1850                 // fix transparent corners
1851                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1852                 return true;
1853             case ICN::NON_UNIFORM_EVIL_CANCEL_BUTTON:
1854                 _icnVsSprite[id].resize( 2 );
1855                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRE, 6 ), 4, 0, 96, 25 );
1856                 _icnVsSprite[id][0].setPosition( 0, 0 );
1857 
1858                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 7 );
1859                 _icnVsSprite[id][1].setPosition( 0, 0 );
1860 
1861                 // fix transparent corners
1862                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1863                 return true;
1864             case ICN::NON_UNIFORM_EVIL_RESTART_BUTTON:
1865                 _icnVsSprite[id].resize( 2 );
1866                 _icnVsSprite[id][0] = Crop( GetICN( ICN::CAMPXTRE, 2 ), 4, 0, 108, 25 );
1867                 _icnVsSprite[id][0].setPosition( 0, 0 );
1868 
1869                 _icnVsSprite[id][1] = GetICN( ICN::CAMPXTRE, 3 );
1870                 _icnVsSprite[id][1].setPosition( 0, 0 );
1871 
1872                 // fix transparent corners
1873                 CopyTransformLayer( _icnVsSprite[id][1], _icnVsSprite[id][0] );
1874                 return true;
1875             case ICN::UNIFORM_GOOD_MAX_BUTTON: {
1876                 _icnVsSprite[id].resize( 2 );
1877 
1878                 // Generate background
1879                 Image background( 60, 25 );
1880                 Copy( GetICN( ICN::SYSTEM, 12 ), 0, 0, background, 0, 0, 53, 25 );
1881                 Copy( GetICN( ICN::SYSTEM, 12 ), 89, 0, background, 53, 0, 7, 25 );
1882 
1883                 // Released button
1884                 Image temp( 60, 25 );
1885                 Copy( GetICN( ICN::SYSTEM, 11 ), 0, 0, temp, 0, 0, 53, 25 );
1886                 Copy( GetICN( ICN::SYSTEM, 11 ), 89, 0, temp, 53, 0, 7, 25 );
1887                 Copy( GetICN( ICN::RECRUIT, 4 ), 9, 2, temp, 4, 2, 52, 19 );
1888 
1889                 _icnVsSprite[id][0] = background;
1890                 Blit( temp, _icnVsSprite[id][0] );
1891                 _icnVsSprite[id][0].setPosition( 0, 0 );
1892 
1893                 // Pressed button
1894                 _icnVsSprite[id][1] = background;
1895                 Copy( GetICN( ICN::RECRUIT, 5 ), 9, 2, _icnVsSprite[id][1], 3, 2, 53, 19 );
1896 
1897                 return true;
1898             }
1899             case ICN::UNIFORM_GOOD_MIN_BUTTON: {
1900                 _icnVsSprite[id].resize( 2 );
1901 
1902                 // Generate background
1903                 Image background( 60, 25 );
1904                 Copy( GetICN( ICN::SYSTEM, 12 ), 0, 0, background, 0, 0, 53, 25 );
1905                 Copy( GetICN( ICN::SYSTEM, 12 ), 89, 0, background, 53, 0, 7, 25 );
1906 
1907                 // Released button
1908                 Image temp( 60, 25 );
1909                 Copy( GetICN( ICN::SYSTEM, 11 ), 0, 0, temp, 0, 0, 53, 25 );
1910                 Copy( GetICN( ICN::SYSTEM, 11 ), 89, 0, temp, 53, 0, 7, 25 );
1911                 Copy( GetICN( ICN::RECRUIT, 4 ), 9, 2, temp, 4, 2, 21, 19 ); // letter 'M'
1912                 Copy( GetICN( ICN::APANEL, 4 ), 23, 21, temp, 28, 5, 8, 14 ); // letter 'I'
1913                 Copy( GetICN( ICN::APANEL, 4 ), 31, 21, temp, 39, 5, 17, 14 ); // letter 'N'
1914 
1915                 _icnVsSprite[id][0] = background;
1916                 Blit( temp, _icnVsSprite[id][0] );
1917                 _icnVsSprite[id][0].setPosition( 0, 0 );
1918 
1919                 // Pressed button
1920                 _icnVsSprite[id][1] = background;
1921                 Copy( GetICN( ICN::RECRUIT, 5 ), 9, 3, _icnVsSprite[id][1], 3, 3, 19, 17 ); // letter 'M'
1922                 Copy( GetICN( ICN::APANEL, 5 ), 21, 22, _icnVsSprite[id][1], 25, 6, 8, 14 ); // letter 'I'
1923                 Copy( GetICN( ICN::APANEL, 5 ), 30, 21, _icnVsSprite[id][1], 37, 5, 17, 15 ); // letter 'N'
1924 
1925                 return true;
1926             }
1927             case ICN::UNIFORM_EVIL_MAX_BUTTON: {
1928                 _icnVsSprite[id].resize( 2 );
1929 
1930                 // Generate background
1931                 Image background( 60, 25 );
1932                 Copy( GetICN( ICN::SYSTEME, 12 ), 0, 0, background, 0, 0, 53, 25 );
1933                 Copy( GetICN( ICN::SYSTEME, 12 ), 89, 0, background, 53, 0, 7, 25 );
1934 
1935                 // Released button
1936                 Image temp( 60, 25 );
1937                 Copy( GetICN( ICN::SYSTEME, 11 ), 0, 0, temp, 0, 0, 53, 25 );
1938                 Copy( GetICN( ICN::SYSTEME, 11 ), 89, 0, temp, 53, 0, 7, 25 );
1939                 Copy( GetICN( ICN::CPANELE, 0 ), 46, 28, temp, 6, 5, 19, 14 ); // letter 'M'
1940                 Copy( GetICN( ICN::CSPANBTE, 0 ), 49, 5, temp, 25, 5, 13, 14 ); // letter 'A'
1941                 Copy( GetICN( ICN::CSPANBTE, 0 ), 62, 10, temp, 38, 10, 2, 9 ); // rest of letter 'A'
1942                 Copy( GetICN( ICN::LGNDXTRE, 4 ), 28, 5, temp, 41, 5, 15, 14 ); // letter 'X'
1943 
1944                 _icnVsSprite[id][0] = background;
1945                 Blit( temp, _icnVsSprite[id][0] );
1946                 _icnVsSprite[id][0].setPosition( 0, 0 );
1947 
1948                 // Pressed button
1949                 _icnVsSprite[id][1] = background;
1950                 Copy( GetICN( ICN::CPANELE, 1 ), 45, 29, _icnVsSprite[id][1], 4, 6, 19, 14 ); // letter 'M'
1951                 Copy( GetICN( ICN::CSPANBTE, 1 ), 49, 6, _icnVsSprite[id][1], 23, 6, 12, 14 ); // letter 'A'
1952                 Copy( GetICN( ICN::CSPANBTE, 1 ), 61, 11, _icnVsSprite[id][1], 35, 11, 3, 9 ); // rest of letter 'A'
1953                 Copy( GetICN( ICN::LGNDXTRE, 5 ), 26, 4, _icnVsSprite[id][1], 38, 4, 15, 16 ); // letter 'X'
1954                 _icnVsSprite[id][1].image()[353] = 21;
1955                 _icnVsSprite[id][1].image()[622] = 21;
1956                 _icnVsSprite[id][1].image()[964] = 21;
1957 
1958                 return true;
1959             }
1960             case ICN::UNIFORM_EVIL_MIN_BUTTON: {
1961                 _icnVsSprite[id].resize( 2 );
1962 
1963                 // Generate background
1964                 Image background( 60, 25 );
1965                 Copy( GetICN( ICN::SYSTEME, 12 ), 0, 0, background, 0, 0, 53, 25 );
1966                 Copy( GetICN( ICN::SYSTEME, 12 ), 89, 0, background, 53, 0, 7, 25 );
1967 
1968                 // Released button
1969                 Image temp( 60, 25 );
1970                 Copy( GetICN( ICN::SYSTEME, 11 ), 0, 0, temp, 0, 0, 53, 25 );
1971                 Copy( GetICN( ICN::SYSTEME, 11 ), 89, 0, temp, 53, 0, 7, 25 );
1972                 Copy( GetICN( ICN::CPANELE, 0 ), 46, 28, temp, 6, 5, 19, 14 ); // letter 'M'
1973                 Copy( GetICN( ICN::APANELE, 4 ), 23, 21, temp, 28, 5, 8, 14 ); // letter 'I'
1974                 Copy( GetICN( ICN::APANELE, 4 ), 31, 21, temp, 39, 5, 17, 14 ); // letter 'N'
1975 
1976                 _icnVsSprite[id][0] = background;
1977                 Blit( temp, _icnVsSprite[id][0] );
1978                 _icnVsSprite[id][0].setPosition( 0, 0 );
1979 
1980                 // Pressed button
1981                 _icnVsSprite[id][1] = background;
1982                 Copy( GetICN( ICN::CPANELE, 1 ), 45, 29, _icnVsSprite[id][1], 4, 6, 19, 14 ); // letter 'M'
1983                 Copy( GetICN( ICN::APANELE, 5 ), 21, 22, _icnVsSprite[id][1], 25, 6, 8, 14 ); // letter 'I'
1984                 Copy( GetICN( ICN::APANELE, 5 ), 30, 21, _icnVsSprite[id][1], 37, 5, 17, 15 ); // letter 'N'
1985                 _icnVsSprite[id][1].image()[622] = 21;
1986                 _icnVsSprite[id][1].image()[964] = 21;
1987                 _icnVsSprite[id][1].image()[1162] = 21;
1988 
1989                 return true;
1990             }
1991             case ICN::WHITE_LARGE_FONT: {
1992                 GetICN( ICN::FONT, 0 );
1993                 const std::vector<Sprite> & original = _icnVsSprite[ICN::FONT];
1994                 _icnVsSprite[id].resize( original.size() );
1995                 for ( size_t i = 0; i < _icnVsSprite[id].size(); ++i ) {
1996                     const Sprite & in = original[i];
1997                     Sprite & out = _icnVsSprite[id][i];
1998                     out.resize( in.width() * 2, in.height() * 2 );
1999                     Resize( in, out, true );
2000                     out.setPosition( in.x() * 2, in.y() * 2 );
2001                 }
2002                 return true;
2003             }
2004             case ICN::SWAP_ARROW_LEFT_TO_RIGHT:
2005             case ICN::SWAP_ARROW_RIGHT_TO_LEFT: {
2006                 // Since the original game does not have such resources we could generate it from hero meeting sprite.
2007                 const Sprite & original = GetICN( ICN::SWAPWIN, 0 );
2008                 std::vector<Image> input( 4 );
2009 
2010                 const int32_t width = 45;
2011                 const int32_t height = 20;
2012 
2013                 for ( Image & image : input )
2014                     image.resize( width, height );
2015 
2016                 Copy( original, 295, 270, input[0], 0, 0, width, height );
2017                 Copy( original, 295, 291, input[1], 0, 0, width, height );
2018                 Copy( original, 295, 363, input[2], 0, 0, width, height );
2019                 Copy( original, 295, 384, input[3], 0, 0, width, height );
2020 
2021                 input[1] = Flip( input[1], true, false );
2022                 input[3] = Flip( input[3], true, false );
2023 
2024                 Image out = ExtractCommonPattern( input );
2025 
2026                 // Here are 2 pixels which should be removed.
2027                 if ( out.width() == width && out.height() == height ) {
2028                     out.image()[40] = 0;
2029                     out.transform()[40] = 1;
2030 
2031                     out.image()[30 + 3 * width] = 0;
2032                     out.transform()[30 + 3 * width] = 1;
2033                 }
2034 
2035                 _icnVsSprite[id].resize( 2 );
2036                 _icnVsSprite[id][0] = ( id == ICN::SWAP_ARROW_LEFT_TO_RIGHT ) ? out : Flip( out, true, false );
2037 
2038                 _icnVsSprite[id][1] = _icnVsSprite[id][0];
2039                 _icnVsSprite[id][1].setPosition( -1, 1 );
2040                 ApplyPalette( _icnVsSprite[id][1], 4 );
2041 
2042                 return true;
2043             }
2044             case ICN::HEROES:
2045                 LoadOriginalICN( id );
2046                 if ( !_icnVsSprite[id].empty() ) {
2047                     // This is the main menu image which shouldn't have any transform layer.
2048                     _icnVsSprite[id][0]._disableTransformLayer();
2049                 }
2050                 return true;
2051             case ICN::TOWNBKG3:
2052                 // Warlock town background image contains 'empty' pixels leading to appear them as black.
2053                 LoadOriginalICN( id );
2054                 if ( !_icnVsSprite[id].empty() ) {
2055                     Sprite & original = _icnVsSprite[id][0];
2056                     if ( original.width() == 640 && original.height() == 256 ) {
2057                         replaceTranformPixel( original, 51945, 17 );
2058                         replaceTranformPixel( original, 61828, 25 );
2059                         replaceTranformPixel( original, 64918, 164 );
2060                         replaceTranformPixel( original, 77685, 18 );
2061                         replaceTranformPixel( original, 84618, 19 );
2062                     }
2063                 }
2064                 return true;
2065             case ICN::PORT0091:
2066                 // Barbarian captain has one bad pixel.
2067                 LoadOriginalICN( id );
2068                 if ( !_icnVsSprite[id].empty() ) {
2069                     Sprite & original = _icnVsSprite[id][0];
2070                     if ( original.width() == 101 && original.height() == 93 ) {
2071                         replaceTranformPixel( original, 9084, 77 );
2072                     }
2073                 }
2074                 return true;
2075             case ICN::PORT0090:
2076                 // Knight captain has multiple bad pixels.
2077                 LoadOriginalICN( id );
2078                 if ( !_icnVsSprite[id].empty() ) {
2079                     Sprite & original = _icnVsSprite[id][0];
2080                     if ( original.width() == 101 && original.height() == 93 ) {
2081                         replaceTranformPixel( original, 2314, 70 );
2082                         replaceTranformPixel( original, 5160, 71 );
2083                         replaceTranformPixel( original, 5827, 18 );
2084                         replaceTranformPixel( original, 7474, 167 );
2085                     }
2086                 }
2087                 return true;
2088             case ICN::CSTLWZRD:
2089                 LoadOriginalICN( id );
2090                 if ( _icnVsSprite[id].size() >= 8 ) {
2091                     // Statue image has bad pixels.
2092                     Sprite & original = _icnVsSprite[id][7];
2093                     if ( original.width() == 135 && original.height() == 57 ) {
2094                         replaceTranformPixel( original, 3687, 50 );
2095                         replaceTranformPixel( original, 5159, 108 );
2096                         replaceTranformPixel( original, 5294, 108 );
2097                     }
2098                 }
2099                 if ( _icnVsSprite[id].size() >= 24 ) {
2100                     // Mage tower image has a bad pixel.
2101                     Sprite & original = _icnVsSprite[id][23];
2102                     if ( original.width() == 135 && original.height() == 57 ) {
2103                         replaceTranformPixel( original, 4333, 23 );
2104                     }
2105                 }
2106                 if ( _icnVsSprite[id].size() >= 29 ) {
2107                     // Mage tower image has a bad pixel.
2108                     Sprite & original = _icnVsSprite[id][28];
2109                     if ( original.width() == 135 && original.height() == 57 ) {
2110                         replaceTranformPixel( original, 4333, 23 );
2111                     }
2112                 }
2113                 return true;
2114             case ICN::CSTLCAPK:
2115                 // Knight captain has a bad pixel.
2116                 LoadOriginalICN( id );
2117                 if ( _icnVsSprite[id].size() >= 2 ) {
2118                     Sprite & original = _icnVsSprite[id][1];
2119                     if ( original.width() == 84 && original.height() == 81 ) {
2120                         replaceTranformPixel( original, 4934, 18 );
2121                     }
2122                 }
2123                 return true;
2124             case ICN::CSTLCAPW:
2125                 // Warlock captain quarters have bad pixels.
2126                 LoadOriginalICN( id );
2127                 if ( !_icnVsSprite[id].empty() ) {
2128                     Sprite & original = _icnVsSprite[id][0];
2129                     if ( original.width() == 84 && original.height() == 81 ) {
2130                         replaceTranformPixel( original, 1692, 26 );
2131                         replaceTranformPixel( original, 2363, 32 );
2132                         replaceTranformPixel( original, 2606, 21 );
2133                         replaceTranformPixel( original, 2608, 21 );
2134                     }
2135                 }
2136                 return true;
2137             case ICN::CSTLSORC:
2138                 LoadOriginalICN( id );
2139                 if ( _icnVsSprite[id].size() >= 14 ) {
2140                     // Rainbow has bad pixels.
2141                     Sprite & original = _icnVsSprite[id][13];
2142                     if ( original.width() == 135 && original.height() == 57 ) {
2143                         replaceTranformPixel( original, 2047, 160 );
2144                         replaceTranformPixel( original, 2052, 159 );
2145                         replaceTranformPixel( original, 2055, 160 );
2146                         replaceTranformPixel( original, 2060, 67 );
2147                         replaceTranformPixel( original, 2063, 159 );
2148                         replaceTranformPixel( original, 2067, 67 );
2149                         replaceTranformPixel( original, 2184, 67 );
2150                         replaceTranformPixel( original, 2192, 158 );
2151                         replaceTranformPixel( original, 3508, 67 );
2152                         replaceTranformPixel( original, 3641, 67 );
2153                         replaceTranformPixel( original, 3773, 69 );
2154                         replaceTranformPixel( original, 3910, 67 );
2155                         replaceTranformPixel( original, 4039, 69 );
2156                         replaceTranformPixel( original, 4041, 67 );
2157                         replaceTranformPixel( original, 4172, 67 );
2158                         replaceTranformPixel( original, 4578, 69 );
2159                     }
2160                 }
2161                 if ( _icnVsSprite[id].size() >= 25 ) {
2162                     // Red tower has bad pixels.
2163                     Sprite & original = _icnVsSprite[id][24];
2164                     if ( original.width() == 135 && original.height() == 57 ) {
2165                         replaceTranformPixel( original, 2830, 165 );
2166                         replaceTranformPixel( original, 3101, 165 );
2167                         replaceTranformPixel( original, 3221, 69 );
2168                     }
2169                 }
2170                 return true;
2171             case ICN::CURSOR_ADVENTURE_MAP: {
2172                 // Create needed numbers
2173                 const std::vector<Point> twoPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 } };
2174                 const std::vector<Point> threePoints = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } };
2175                 const std::vector<Point> fourPoints = { { 1, 1 }, { 3, 1 }, { 1, 2 }, { 3, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 3 }, { 3, 4 }, { 3, 5 } };
2176                 const std::vector<Point> fivePoints
2177                     = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 4, 4 }, { 1, 5 }, { 2, 5 }, { 3, 5 } };
2178                 const std::vector<Point> sixPoints = { { 2, 1 }, { 3, 1 }, { 1, 2 }, { 1, 3 }, { 2, 3 }, { 3, 3 }, { 1, 4 }, { 4, 4 }, { 2, 5 }, { 3, 5 } };
2179                 const std::vector<Point> sevenPoints = { { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 4, 2 }, { 3, 3 }, { 2, 4 }, { 2, 5 } };
2180                 const std::vector<Point> plusPoints = { { 2, 1 }, { 1, 2 }, { 2, 2 }, { 3, 2 }, { 2, 3 } };
2181 
2182                 std::vector<Image> digits( 7 );
2183                 digits[0] = createDigit( 6, 7, twoPoints );
2184                 digits[1] = createDigit( 6, 7, threePoints );
2185                 digits[2] = createDigit( 6, 7, fourPoints );
2186                 digits[3] = createDigit( 6, 7, fivePoints );
2187                 digits[4] = createDigit( 6, 7, sixPoints );
2188                 digits[5] = createDigit( 6, 7, sevenPoints );
2189                 digits[6] = addDigit( digits[5], createDigit( 5, 5, plusPoints ), Point( -1, -1 ) );
2190 
2191                 _icnVsSprite[id].reserve( 7 * 8 );
2192 
2193                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 4 ), digits, Point( -2, 1 ) );
2194                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 5 ), digits, Point( 1, 1 ) );
2195                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 6 ), digits, Point( 0, 1 ) );
2196                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 7 ), digits, Point( -2, 1 ) );
2197                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 8 ), digits, Point( 1, 1 ) );
2198                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 9 ), digits, Point( -6, 1 ) );
2199                 populateCursorIcons( _icnVsSprite[id], GetICN( ICN::ADVMCO, 28 ), digits, Point( 0, 1 ) );
2200 
2201                 return true;
2202             }
2203             case ICN::DISMISS_HERO_DISABLED_BUTTON:
2204             case ICN::NEW_CAMPAIGN_DISABLED_BUTTON:
2205             case ICN::MAX_DISABLED_BUTTON: {
2206                 _icnVsSprite[id].resize( 1 );
2207                 Sprite & output = _icnVsSprite[id][0];
2208 
2209                 int buttonIcnId = ICN::UNKNOWN;
2210                 uint32_t startIcnId = 0;
2211 
2212                 if ( id == ICN::DISMISS_HERO_DISABLED_BUTTON ) {
2213                     buttonIcnId = ICN::HSBTNS;
2214                     startIcnId = 0;
2215                 }
2216                 else if ( id == ICN::NEW_CAMPAIGN_DISABLED_BUTTON ) {
2217                     buttonIcnId = ICN::BTNNEWGM;
2218                     startIcnId = 2;
2219                 }
2220                 else if ( id == ICN::MAX_DISABLED_BUTTON ) {
2221                     buttonIcnId = ICN::RECRUIT;
2222                     startIcnId = 4;
2223                 }
2224 
2225                 assert( buttonIcnId != ICN::UNKNOWN ); // Did you add a new disabled button and forget to add the condition above?
2226 
2227                 const Sprite & released = GetICN( buttonIcnId, startIcnId );
2228                 const Sprite & pressed = GetICN( buttonIcnId, startIcnId + 1 );
2229                 output = released;
2230 
2231                 ApplyPalette( output, PAL::GetPalette( PAL::PaletteType::DARKENING ) );
2232 
2233                 Image common = ExtractCommonPattern( { released, pressed } );
2234                 common = FilterOnePixelNoise( common );
2235                 common = FilterOnePixelNoise( common );
2236                 common = FilterOnePixelNoise( common );
2237 
2238                 Blit( common, output );
2239                 return true;
2240             }
2241             case ICN::KNIGHT_CASTLE_RIGHT_FARM: {
2242                 _icnVsSprite[id].resize( 1 );
2243                 Sprite & output = _icnVsSprite[id][0];
2244                 output = GetICN( ICN::TWNKWEL2, 0 );
2245 
2246                 ApplyPalette( output, 28, 21, output, 28, 21, 39, 1, 8 );
2247                 ApplyPalette( output, 0, 22, output, 0, 22, 69, 1, 8 );
2248                 ApplyPalette( output, 0, 23, output, 0, 23, 53, 1, 8 );
2249                 ApplyPalette( output, 0, 24, output, 0, 24, 54, 1, 8 );
2250                 ApplyPalette( output, 0, 25, output, 0, 25, 62, 1, 8 );
2251                 return true;
2252             }
2253             case ICN::KNIGHT_CASTLE_LEFT_FARM:
2254                 _icnVsSprite[id].resize( 1 );
2255                 h2d::readImage( "knight_castle_left_farm.image", _icnVsSprite[id][0] );
2256                 return true;
2257             case ICN::BARBARIAN_CASTLE_CAPTAIN_QUARTERS_LEFT_SIDE:
2258                 _icnVsSprite[id].resize( 1 );
2259                 h2d::readImage( "barbarian_castle_captain_quarter_left_side.image", _icnVsSprite[id][0] );
2260                 return true;
2261             case ICN::NECROMANCER_CASTLE_STANDALONE_CAPTAIN_QUARTERS: {
2262                 _icnVsSprite[id].resize( 1 );
2263                 Sprite & output = _icnVsSprite[id][0];
2264                 const Sprite & original = GetICN( ICN::TWNNCAPT, 0 );
2265 
2266                 output = Crop( original, 21, 0, original.width() - 21, original.height() );
2267                 output.setPosition( original.x() + 21, original.y() );
2268 
2269                 for ( int32_t y = 47; y < output.height(); ++y ) {
2270                     SetTransformPixel( output, 0, y, 1 );
2271                 }
2272 
2273                 const Sprite & castle = GetICN( ICN::TWNNCSTL, 0 );
2274                 Copy( castle, 402, 123, output, 1, 56, 2, 11 );
2275 
2276                 return true;
2277             }
2278             case ICN::NECROMANCER_CASTLE_CAPTAIN_QUARTERS_BRIDGE: {
2279                 _icnVsSprite[id].resize( 1 );
2280                 Sprite & output = _icnVsSprite[id][0];
2281                 const Sprite & original = GetICN( ICN::TWNNCAPT, 0 );
2282 
2283                 output = Crop( original, 0, 0, 23, original.height() );
2284                 output.setPosition( original.x(), original.y() );
2285 
2286                 return true;
2287             }
2288             case ICN::ESCROLL:
2289                 LoadOriginalICN( id );
2290                 if ( _icnVsSprite[id].size() > 4 ) {
2291                     // fix missing black border on the right side of the "up" button
2292                     Sprite & out = _icnVsSprite[id][4];
2293                     if ( out.width() == 16 && out.height() == 16 ) {
2294                         Copy( out, 0, 0, out, 15, 0, 1, 16 );
2295                     }
2296                 }
2297                 return true;
2298             case ICN::MAP_TYPE_ICON: {
2299                 // TODO: add a new icon for the Resurrection add-on map type.
2300                 _icnVsSprite[id].resize( 2 );
2301                 for ( Sprite & icon : _icnVsSprite[id] ) {
2302                     icon.resize( 17, 17 );
2303                     icon.fill( 0 );
2304                 }
2305 
2306                 const Sprite & successionWarsIcon = GetICN( ICN::ARTFX, 6 );
2307                 const Sprite & priceOfLoyaltyIcon = GetICN( ICN::ARTFX, 90 );
2308 
2309                 if ( !successionWarsIcon.empty() ) {
2310                     Resize( successionWarsIcon, 0, 0, successionWarsIcon.width(), successionWarsIcon.height(), _icnVsSprite[id][0], 1, 1, 15, 15 );
2311                 }
2312 
2313                 if ( !priceOfLoyaltyIcon.empty() ) {
2314                     Resize( priceOfLoyaltyIcon, 0, 0, priceOfLoyaltyIcon.width(), priceOfLoyaltyIcon.height(), _icnVsSprite[id][1], 1, 1, 15, 15 );
2315                 }
2316 
2317                 return true;
2318             }
2319             case ICN::TWNWWEL2: {
2320                 LoadOriginalICN( id );
2321                 if ( _icnVsSprite[id].size() == 7 ) {
2322                     if ( _icnVsSprite[id][0].width() == 122 && _icnVsSprite[id][0].height() == 226 ) {
2323                         FillTransform( _icnVsSprite[id][0], 0, 57, 56, 62, 1 );
2324                     }
2325 
2326                     for ( size_t i = 1; i < 7; i++ ) {
2327                         Sprite & original = _icnVsSprite[id][i];
2328                         if ( original.width() == 121 && original.height() == 151 ) {
2329                             FillTransform( original, 0, 0, 64, 39, 1 );
2330                         }
2331                     }
2332                 }
2333                 return true;
2334             }
2335             case ICN::TWNWCAPT: {
2336                 LoadOriginalICN( id );
2337                 if ( !_icnVsSprite[id].empty() ) {
2338                     Sprite & original = _icnVsSprite[id][0];
2339                     if ( original.width() == 118 && original.height() ) {
2340                         // Remove shadow from left side.
2341                         FillTransform( original, 85, 84, 33, 26, 1 );
2342 
2343                         // Remove extra terrain at the bottom.
2344                         FillTransform( original, 0, 114, 40, 4, 1 );
2345                         FillTransform( original, 9, 112, 51, 2, 1 );
2346                         FillTransform( original, 35, 110, 47, 2, 1 );
2347                         FillTransform( original, 57, 108, 51, 2, 1 );
2348                     }
2349                 }
2350                 return true;
2351             }
2352             case ICN::GOOD_ARMY_BUTTON:
2353             case ICN::GOOD_MARKET_BUTTON: {
2354                 _icnVsSprite[id].resize( 2 );
2355 
2356                 const int releasedIndex = ( id == ICN::GOOD_ARMY_BUTTON ) ? 0 : 4;
2357                 _icnVsSprite[id][0] = GetICN( ICN::ADVBTNS, releasedIndex );
2358                 _icnVsSprite[id][1] = GetICN( ICN::ADVBTNS, releasedIndex + 1 );
2359                 AddTransparency( _icnVsSprite[id][0], 36 );
2360                 AddTransparency( _icnVsSprite[id][1], 36 );
2361                 AddTransparency( _icnVsSprite[id][1], 61 ); // remove the extra brown border
2362 
2363                 return true;
2364             }
2365             case ICN::EVIL_ARMY_BUTTON:
2366             case ICN::EVIL_MARKET_BUTTON: {
2367                 _icnVsSprite[id].resize( 2 );
2368 
2369                 const int releasedIndex = ( id == ICN::EVIL_ARMY_BUTTON ) ? 0 : 4;
2370                 _icnVsSprite[id][0] = GetICN( ICN::ADVEBTNS, releasedIndex );
2371                 AddTransparency( _icnVsSprite[id][0], 36 );
2372 
2373                 Sprite pressed = GetICN( ICN::ADVEBTNS, releasedIndex + 1 );
2374                 AddTransparency( pressed, 36 );
2375                 AddTransparency( pressed, 61 ); // remove the extra brown border
2376 
2377                 _icnVsSprite[id][1] = Sprite( pressed.width(), pressed.height(), pressed.x(), pressed.y() );
2378                 _icnVsSprite[id][1].reset();
2379 
2380                 // put back pixels that actually should be black
2381                 Fill( _icnVsSprite[id][1], 1, 4, 31, 31, 36 );
2382                 Blit( pressed, _icnVsSprite[id][1] );
2383 
2384                 return true;
2385             }
2386             case ICN::SPANBTN:
2387             case ICN::SPANBTNE:
2388             case ICN::CSPANBTN:
2389             case ICN::CSPANBTE: {
2390                 LoadOriginalICN( id );
2391                 if ( !_icnVsSprite[id].empty() ) {
2392                     // add missing part of the released button state on the left
2393                     Sprite & out = _icnVsSprite[id][0];
2394 
2395                     Sprite released( out.width() + 1, out.height() );
2396                     released.reset();
2397                     const uint8_t color = id == ICN::SPANBTN || id == ICN::CSPANBTN ? 57 : 32;
2398                     DrawLine( released, Point( 0, 3 ), Point( 0, out.height() - 1 ), color );
2399                     Blit( out, released, 1, 0 );
2400 
2401                     out = std::move( released );
2402                 }
2403                 return true;
2404             }
2405             case ICN::TRADPOSE: {
2406                 LoadOriginalICN( id );
2407                 if ( _icnVsSprite[id].size() >= 19 ) {
2408                     // fix background for TRADE and EXIT buttons
2409                     for ( uint32_t i : { 16, 18 } ) {
2410                         Sprite pressed;
2411                         std::swap( pressed, _icnVsSprite[id][i] );
2412                         AddTransparency( pressed, 25 ); // remove too dark background
2413 
2414                         // take background from the empty system button
2415                         _icnVsSprite[id][i] = GetICN( ICN::SYSTEME, 12 );
2416 
2417                         // put back dark-gray pixels in the middle of the button
2418                         Fill( _icnVsSprite[id][i], 5, 5, 86, 17, 25 );
2419                         Blit( pressed, _icnVsSprite[id][i] );
2420                     }
2421                 }
2422                 return true;
2423             }
2424             case ICN::RECRUIT: {
2425                 LoadOriginalICN( id );
2426                 if ( _icnVsSprite[id].size() >= 10 ) {
2427                     // fix transparent corners on released OKAY button
2428                     CopyTransformLayer( _icnVsSprite[id][9], _icnVsSprite[id][8] );
2429                 }
2430                 return true;
2431             }
2432             case ICN::NGEXTRA: {
2433                 LoadOriginalICN( id );
2434                 if ( _icnVsSprite[id].size() >= 70 ) {
2435                     // fix transparent corners on pressed OKAY and CANCEL buttons
2436                     CopyTransformLayer( _icnVsSprite[id][66], _icnVsSprite[id][67] );
2437                     CopyTransformLayer( _icnVsSprite[id][68], _icnVsSprite[id][69] );
2438                 }
2439                 return true;
2440             }
2441             case ICN::HSBTNS: {
2442                 LoadOriginalICN( id );
2443                 if ( _icnVsSprite[id].size() >= 4 ) {
2444                     // extract the EXIT button without background
2445                     Image exitReleased = _icnVsSprite[id][2];
2446                     Image exitPressed = _icnVsSprite[id][3];
2447 
2448                     // make the border parts around EXIT button transparent
2449                     Image exitCommonMask = ExtractCommonPattern( { exitReleased, exitPressed } );
2450                     invertTransparency( exitCommonMask );
2451 
2452                     CopyTransformLayer( exitCommonMask, exitReleased );
2453                     CopyTransformLayer( exitCommonMask, exitPressed );
2454 
2455                     // fix DISMISS button: get the EXIT button, then slap the text back
2456                     Sprite & dismissReleased = _icnVsSprite[id][0];
2457 
2458                     Sprite tmpReleased = dismissReleased;
2459                     Blit( exitReleased, 0, 0, tmpReleased, 5, 0, 27, 120 );
2460                     Blit( dismissReleased, 9, 4, tmpReleased, 9, 4, 19, 110 );
2461 
2462                     dismissReleased = std::move( tmpReleased );
2463 
2464                     Sprite & dismissPressed = _icnVsSprite[id][1];
2465 
2466                     // start with the released state as well to capture more details
2467                     Sprite tmpPressed = dismissReleased;
2468                     Blit( exitPressed, 0, 0, tmpPressed, 5, 0, 27, 120 );
2469                     Blit( dismissPressed, 9, 5, tmpPressed, 8, 5, 19, 110 );
2470 
2471                     dismissPressed = std::move( tmpPressed );
2472                 }
2473                 return true;
2474             }
2475             default:
2476                 break;
2477             }
2478 
2479             return false;
2480         }
2481 
GetMaximumICNIndex(int id)2482         size_t GetMaximumICNIndex( int id )
2483         {
2484             if ( _icnVsSprite[id].empty() && !LoadModifiedICN( id ) ) {
2485                 LoadOriginalICN( id );
2486             }
2487 
2488             return _icnVsSprite[id].size();
2489         }
2490 
GetMaximumTILIndex(int id)2491         size_t GetMaximumTILIndex( int id )
2492         {
2493             if ( _tilVsImage[id].empty() ) {
2494                 _tilVsImage[id].resize( 4 ); // 4 possible sides
2495 
2496                 const std::vector<uint8_t> & data = ::AGG::ReadChunk( tilFileName[id] );
2497                 if ( data.size() < headerSize ) {
2498                     return 0;
2499                 }
2500 
2501                 StreamBuf buffer( data );
2502 
2503                 const uint32_t count = buffer.getLE16();
2504                 const uint32_t width = buffer.getLE16();
2505                 const uint32_t height = buffer.getLE16();
2506                 const uint32_t size = width * height;
2507                 if ( headerSize + count * size != data.size() ) {
2508                     return 0;
2509                 }
2510 
2511                 std::vector<Image> & originalTIL = _tilVsImage[id][0];
2512 
2513                 originalTIL.resize( count );
2514                 for ( uint32_t i = 0; i < count; ++i ) {
2515                     Image & tilImage = originalTIL[i];
2516                     tilImage.resize( width, height );
2517                     tilImage._disableTransformLayer();
2518                     memcpy( tilImage.image(), data.data() + headerSize + i * size, size );
2519                     std::fill( tilImage.transform(), tilImage.transform() + width * height, 0 );
2520                 }
2521 
2522                 for ( uint32_t shapeId = 1; shapeId < 4; ++shapeId ) {
2523                     std::vector<Image> & currentTIL = _tilVsImage[id][shapeId];
2524                     currentTIL.resize( count );
2525 
2526                     const bool horizontalFlip = ( shapeId & 2 ) != 0;
2527                     const bool verticalFlip = ( shapeId & 1 ) != 0;
2528 
2529                     for ( uint32_t i = 0; i < count; ++i ) {
2530                         currentTIL[i] = Flip( originalTIL[i], horizontalFlip, verticalFlip );
2531                     }
2532                 }
2533             }
2534 
2535             return _tilVsImage[id][0].size();
2536         }
2537 
2538         // We have few ICNs which we need to scale like some related to main screen
IsScalableICN(int id)2539         bool IsScalableICN( int id )
2540         {
2541             return id == ICN::HEROES || id == ICN::BTNSHNGL || id == ICN::SHNGANIM;
2542         }
2543 
GetScaledICN(int icnId,uint32_t index)2544         const Sprite & GetScaledICN( int icnId, uint32_t index )
2545         {
2546             const Sprite & originalIcn = _icnVsSprite[icnId][index];
2547 
2548             if ( Display::DEFAULT_WIDTH == Display::instance().width() && Display::DEFAULT_HEIGHT == Display::instance().height() ) {
2549                 return originalIcn;
2550             }
2551 
2552             if ( _icnVsScaledSprite[icnId].empty() ) {
2553                 _icnVsScaledSprite[icnId].resize( _icnVsSprite[icnId].size() );
2554             }
2555 
2556             Sprite & resizedIcn = _icnVsScaledSprite[icnId][index];
2557 
2558             const double scaleFactorX = static_cast<double>( Display::instance().width() ) / Display::DEFAULT_WIDTH;
2559             const double scaleFactorY = static_cast<double>( Display::instance().height() ) / Display::DEFAULT_HEIGHT;
2560 
2561             const int32_t resizedWidth = static_cast<int32_t>( originalIcn.width() * scaleFactorX + 0.5 );
2562             const int32_t resizedHeight = static_cast<int32_t>( originalIcn.height() * scaleFactorY + 0.5 );
2563             // Resize only if needed
2564             if ( resizedIcn.width() != resizedWidth || resizedIcn.height() != resizedHeight ) {
2565                 resizedIcn.resize( resizedWidth, resizedHeight );
2566                 resizedIcn.setPosition( static_cast<int32_t>( originalIcn.x() * scaleFactorX + 0.5 ), static_cast<int32_t>( originalIcn.y() * scaleFactorY + 0.5 ) );
2567                 Resize( originalIcn, resizedIcn, false );
2568             }
2569 
2570             return resizedIcn;
2571         }
2572 
GetICN(int icnId,uint32_t index)2573         const Sprite & GetICN( int icnId, uint32_t index )
2574         {
2575             if ( !IsValidICNId( icnId ) ) {
2576                 return errorImage;
2577             }
2578 
2579             if ( index >= GetMaximumICNIndex( icnId ) ) {
2580                 return errorImage;
2581             }
2582 
2583             if ( IsScalableICN( icnId ) ) {
2584                 return GetScaledICN( icnId, index );
2585             }
2586 
2587             return _icnVsSprite[icnId][index];
2588         }
2589 
GetICNCount(int icnId)2590         uint32_t GetICNCount( int icnId )
2591         {
2592             if ( !IsValidICNId( icnId ) ) {
2593                 return 0;
2594             }
2595 
2596             return static_cast<uint32_t>( GetMaximumICNIndex( icnId ) );
2597         }
2598 
GetTIL(int tilId,uint32_t index,uint32_t shapeId)2599         const Image & GetTIL( int tilId, uint32_t index, uint32_t shapeId )
2600         {
2601             if ( shapeId > 3 ) {
2602                 return errorImage;
2603             }
2604 
2605             if ( !IsValidTILId( tilId ) ) {
2606                 return errorImage;
2607             }
2608 
2609             const size_t maxTILIndex = GetMaximumTILIndex( tilId );
2610             if ( index >= maxTILIndex ) {
2611                 return errorImage;
2612             }
2613 
2614             return _tilVsImage[tilId][shapeId][index];
2615         }
2616 
GetLetter(uint32_t character,uint32_t fontType)2617         const Sprite & GetLetter( uint32_t character, uint32_t fontType )
2618         {
2619             if ( character < 0x21 ) {
2620                 return errorImage;
2621             }
2622 
2623             // TODO: correct naming and standartise the code
2624             switch ( fontType ) {
2625             case Font::GRAY_BIG:
2626                 return GetICN( ICN::GRAY_FONT, character - 0x20 );
2627             case Font::GRAY_SMALL:
2628                 return GetICN( ICN::GRAY_SMALL_FONT, character - 0x20 );
2629             case Font::YELLOW_BIG:
2630                 return GetICN( ICN::YELLOW_FONT, character - 0x20 );
2631             case Font::YELLOW_SMALL:
2632                 return GetICN( ICN::YELLOW_SMALLFONT, character - 0x20 );
2633             case Font::BIG:
2634                 return GetICN( ICN::FONT, character - 0x20 );
2635             case Font::SMALL:
2636                 return GetICN( ICN::SMALFONT, character - 0x20 );
2637             case Font::WHITE_LARGE:
2638                 return GetICN( ICN::WHITE_LARGE_FONT, character - 0x20 );
2639             default:
2640                 break;
2641             }
2642 
2643             return GetICN( ICN::SMALFONT, character - 0x20 );
2644         }
2645 
ASCIILastSupportedCharacter(const uint32_t fontType)2646         uint32_t ASCIILastSupportedCharacter( const uint32_t fontType )
2647         {
2648             switch ( fontType ) {
2649             case Font::BIG:
2650             case Font::GRAY_BIG:
2651             case Font::YELLOW_BIG:
2652             case Font::WHITE_LARGE:
2653                 return static_cast<uint32_t>( GetMaximumICNIndex( ICN::FONT ) ) + 0x20 - 1;
2654             case Font::SMALL:
2655             case Font::GRAY_SMALL:
2656             case Font::YELLOW_SMALL:
2657                 return static_cast<uint32_t>( GetMaximumICNIndex( ICN::SMALFONT ) ) + 0x20 - 1;
2658             default:
2659                 return 0;
2660             }
2661         }
2662 
GetAbsoluteICNHeight(int icnId)2663         int32_t GetAbsoluteICNHeight( int icnId )
2664         {
2665             const uint32_t frameCount = GetICNCount( icnId );
2666             if ( frameCount == 0 ) {
2667                 return 0;
2668             }
2669 
2670             int32_t height = 0;
2671             for ( uint32_t i = 0; i < frameCount; ++i ) {
2672                 const int32_t offset = -GetICN( icnId, i ).y();
2673                 if ( offset > height ) {
2674                     height = offset;
2675                 }
2676             }
2677 
2678             return height;
2679         }
2680 
getCharacterLimit(const FontSize fontSize)2681         uint32_t getCharacterLimit( const FontSize fontSize )
2682         {
2683             switch ( fontSize ) {
2684             case FontSize::SMALL:
2685                 return static_cast<uint32_t>( GetMaximumICNIndex( ICN::SMALFONT ) ) + 0x20 - 1;
2686             case FontSize::NORMAL:
2687             case FontSize::LARGE:
2688                 return static_cast<uint32_t>( GetMaximumICNIndex( ICN::FONT ) ) + 0x20 - 1;
2689             default:
2690                 assert( 0 ); // Did you add a new font size? Please add implementation.
2691             }
2692 
2693             return 0;
2694         }
2695 
getChar(const uint8_t character,const FontType & fontType)2696         const Sprite & getChar( const uint8_t character, const FontType & fontType )
2697         {
2698             if ( character < 0x21 ) {
2699                 return errorImage;
2700             }
2701 
2702             switch ( fontType.size ) {
2703             case FontSize::SMALL:
2704                 switch ( fontType.color ) {
2705                 case FontColor::WHITE:
2706                     return GetICN( ICN::SMALFONT, character - 0x20 );
2707                 case FontColor::GRAY:
2708                     return GetICN( ICN::GRAY_SMALL_FONT, character - 0x20 );
2709                 case FontColor::YELLOW:
2710                     return GetICN( ICN::YELLOW_SMALLFONT, character - 0x20 );
2711                 default:
2712                     break;
2713                 }
2714                 break;
2715             case FontSize::NORMAL:
2716                 switch ( fontType.color ) {
2717                 case FontColor::WHITE:
2718                     return GetICN( ICN::FONT, character - 0x20 );
2719                 case FontColor::GRAY:
2720                     return GetICN( ICN::GRAY_FONT, character - 0x20 );
2721                 case FontColor::YELLOW:
2722                     return GetICN( ICN::YELLOW_FONT, character - 0x20 );
2723                 default:
2724                     break;
2725                 }
2726                 break;
2727             case FontSize::LARGE:
2728                 switch ( fontType.color ) {
2729                 case FontColor::WHITE:
2730                     return GetICN( ICN::WHITE_LARGE_FONT, character - 0x20 );
2731                 default:
2732                     break;
2733                 }
2734                 break;
2735             default:
2736                 break;
2737             }
2738 
2739             assert( 0 ); // Did you add a new font size? Please add implementation.
2740 
2741             return errorImage;
2742         }
2743 
updateAlphabet(const SupportedLanguage language,const bool loadOriginalAlphabet)2744         void updateAlphabet( const SupportedLanguage language, const bool loadOriginalAlphabet )
2745         {
2746             if ( loadOriginalAlphabet || !isAlphabetSupported( language ) ) {
2747                 alphabetPreserver.restore();
2748             }
2749             else {
2750                 alphabetPreserver.preserve();
2751                 generateAlphabet( language );
2752             }
2753         }
2754 
isAlphabetSupported(const SupportedLanguage language)2755         bool isAlphabetSupported( const SupportedLanguage language )
2756         {
2757             switch ( language ) {
2758             case SupportedLanguage::Polish:
2759             case SupportedLanguage::German:
2760             case SupportedLanguage::French:
2761             case SupportedLanguage::Russian:
2762                 return true;
2763             default:
2764                 break;
2765             }
2766 
2767             return false;
2768         }
2769     }
2770 }
2771