OpenTTD: src/gfxinit.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

gfxinit.cpp
Go to the documentation of this file.
1 /* $Id: gfxinit.cpp 26919 2014-09-25 16:04:02Z peter1138 $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #include "stdafx.h"
13 #include "fios.h"
14 #include "newgrf.h"
15 #include "3rdparty/md5/md5.h"
16 #include "fontcache.h"
17 #include "gfx_func.h"
18 #include "transparency.h"
19 #include "blitter/factory.hpp"
20 #include "video/video_driver.hpp"
21 #include "window_func.h"
22
23 /* The type of set we're replacing */
24 #define SET_TYPE "graphics"
25 #include "base_media_func.h"
26
27 #include "table/sprites.h"
28
29 #include "safeguards.h"
30
32 bool _palette_remap_grf[MAX_FILE_SLOTS];
33
34 #include "table/landscape_sprite.h"
35
37 static const SpriteID * const _landscape_spriteindexes[] = {
38 _landscape_spriteindexes_arctic,
39 _landscape_spriteindexes_tropic,
40 _landscape_spriteindexes_toyland,
41 };
42
50 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
51 {
52 uint load_index_org = load_index;
53 uint sprite_id = 0;
54
55 FioOpenFile(file_index, filename, BASESET_DIR);
56
57 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
58
59 byte container_ver = GetGRFContainerVersion();
60 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
61 ReadGRFSpriteOffsets(container_ver);
62 if (container_ver >= 2) {
63 /* Read compression. */
64 byte compression = FioReadByte();
65 if (compression != 0) usererror("Unsupported compression format");
66 }
67
68 while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
69 load_index++;
70 sprite_id++;
71 if (load_index >= MAX_SPRITES) {
72 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
73 }
74 }
75 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
76
77 return load_index - load_index_org;
78 }
79
87 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
88 {
89 uint start;
90 uint sprite_id = 0;
91
92 FioOpenFile(file_index, filename, BASESET_DIR);
93
94 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
95
96 byte container_ver = GetGRFContainerVersion();
97 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
98 ReadGRFSpriteOffsets(container_ver);
99 if (container_ver >= 2) {
100 /* Read compression. */
101 byte compression = FioReadByte();
102 if (compression != 0) usererror("Unsupported compression format");
103 }
104
105 while ((start = *index_tbl++) != END) {
106 uint end = *index_tbl++;
107
108 do {
109 bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
110 assert(b);
111 sprite_id++;
112 } while (++start <= end);
113 }
114 }
115
121 void CheckExternalFiles()
122 {
123 if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
124
125 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
126
127 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
128
129 static const size_t ERROR_MESSAGE_LENGTH = 256;
130 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
131
132 /* Allocate for a message for each missing file and for one error
133 * message per set.
134 */
135 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
136 error_msg[0] = '\0';
137 char *add_pos = error_msg;
138 const char *last = lastof(error_msg);
139
140 if (used_set->GetNumInvalid() != 0) {
141 /* Not all files were loaded successfully, see which ones */
142 add_pos += seprintf(add_pos, last, "Trying to load graphics set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one. See section 4.1 of readme.txt.\n\nThe following files are corrupted or missing:\n", used_set->name);
143 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
144 MD5File::ChecksumResult res = GraphicsSet::CheckMD5(&used_set->files[i], BASESET_DIR);
145 if (res != MD5File::CR_MATCH) add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", used_set->files[i].filename, res == MD5File::CR_MISMATCH ? "corrupt" : "missing", used_set->files[i].missing_warning);
146 }
147 add_pos += seprintf(add_pos, last, "\n");
148 }
149
150 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
151 if (sounds_set->GetNumInvalid() != 0) {
152 add_pos += seprintf(add_pos, last, "Trying to load sound set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one. See section 4.1 of readme.txt.\n\nThe following files are corrupted or missing:\n", sounds_set->name);
153
154 assert_compile(SoundsSet::NUM_FILES == 1);
155 /* No need to loop each file, as long as there is only a single
156 * sound file. */
157 add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, SoundsSet::CheckMD5(sounds_set->files, BASESET_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
158 }
159
160 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
161 }
162
164 static void LoadSpriteTables()
165 {
166 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
167 uint i = FIRST_GRF_SLOT;
168 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
169
170 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
171 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
172
173 /*
174 * The second basic file always starts at the given location and does
175 * contain a different amount of sprites depending on the "type"; DOS
176 * has a few sprites less. However, we do not care about those missing
177 * sprites as they are not shown anyway (logos in intro game).
178 */
179 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
180 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
181
182 /*
183 * Load additional sprites for climates other than temperate.
184 * This overwrites some of the temperate sprites, such as foundations
185 * and the ground sprites.
186 */
187 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
188 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
189 LoadGrfFileIndexed(
190 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
191 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
192 i++
193 );
194 }
195
196 /* Initialize the unicode to sprite mapping table */
197 InitializeUnicodeGlyphMap();
198
199 /*
200 * Load the base NewGRF with OTTD required graphics as first NewGRF.
201 * However, we do not want it to show up in the list of used NewGRFs,
202 * so we have to manually add it, and then remove it later.
203 */
204 GRFConfig *top = _grfconfig;
205 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
206
207 /* We know the palette of the base set, so if the base NewGRF is not
208 * setting one, use the palette of the base set and not the global
209 * one which might be the wrong palette for this base NewGRF.
210 * The value set here might be overridden via action14 later. */
211 switch (used_set->palette) {
212 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
213 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
214 default: break;
215 }
216 FillGRFDetails(master, false, BASESET_DIR);
217
218 ClrBit(master->flags, GCF_INIT_ONLY);
219 master->next = top;
220 _grfconfig = master;
221
222 LoadNewGRF(SPR_NEWGRFS_BASE, i);
223
224 /* Free and remove the top element. */
225 delete master;
226 _grfconfig = top;
227 }
228
229
234 static bool SwitchNewGRFBlitter()
235 {
236 /* Never switch if the blitter was specified by the user. */
237 if (!_blitter_autodetected) return false;
238
239 /* Null driver => dedicated server => do nothing. */
240 if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return false;
241
242 /* Get preferred depth.
243 * - depth_wanted_by_base: Depth required by the baseset, i.e. the majority of the sprites.
244 * - depth_wanted_by_grf: Depth required by some NewGRF.
245 * Both can force using a 32bpp blitter. depth_wanted_by_base is used to select
246 * between multiple 32bpp blitters, which perform differently with 8bpp sprites.
247 */
248 uint depth_wanted_by_base = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP ? 32 : 8;
249 uint depth_wanted_by_grf = _support8bpp == S8BPP_NONE ? 32 : 8;
250 for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
251 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
252 if (c->palette & GRFP_BLT_32BPP) depth_wanted_by_grf = 32;
253 }
254
255 /* Search the best blitter. */
256 static const struct {
257 const char *name;
258 uint animation;
259 uint min_base_depth, max_base_depth, min_grf_depth, max_grf_depth;
260 } replacement_blitters[] = {
261 #ifdef WITH_SSE
262 { "32bpp-sse4", 0, 32, 32, 8, 32 },
263 { "32bpp-ssse3", 0, 32, 32, 8, 32 },
264 { "32bpp-sse2", 0, 32, 32, 8, 32 },
265 { "32bpp-sse4-anim", 1, 32, 32, 8, 32 },
266 #endif
267 { "8bpp-optimized", 2, 8, 8, 8, 8 },
268 { "32bpp-optimized", 0, 8, 32, 8, 32 },
269 { "32bpp-anim", 1, 8, 32, 8, 32 },
270 };
271
272 const bool animation_wanted = HasBit(_display_opt, DO_FULL_ANIMATION);
273 const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
274
275 for (uint i = 0; i < lengthof(replacement_blitters); i++) {
276 if (animation_wanted && (replacement_blitters[i].animation == 0)) continue;
277 if (!animation_wanted && (replacement_blitters[i].animation == 1)) continue;
278
279 if (!IsInsideMM(depth_wanted_by_base, replacement_blitters[i].min_base_depth, replacement_blitters[i].max_base_depth + 1)) continue;
280 if (!IsInsideMM(depth_wanted_by_grf, replacement_blitters[i].min_grf_depth, replacement_blitters[i].max_grf_depth + 1)) continue;
281 const char *repl_blitter = replacement_blitters[i].name;
282
283 if (strcmp(repl_blitter, cur_blitter) == 0) return false;
284 if (BlitterFactory::GetBlitterFactory(repl_blitter) == NULL) continue;
285
286 DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
287 Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
288 if (new_blitter == NULL) NOT_REACHED();
289 DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter);
290 break;
291 }
292
293 if (!VideoDriver::GetInstance()->AfterBlitterChange()) {
294 /* Failed to switch blitter, let's hope we can return to the old one. */
295 if (BlitterFactory::SelectBlitter(cur_blitter) == NULL || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
296 }
297
298 return true;
299 }
300
302 void CheckBlitter()
303 {
304 if (!SwitchNewGRFBlitter()) return;
305
306 ClearFontCache();
307 GfxClearSpriteCache();
308 ReInitAllWindows();
309 }
310
312 void GfxLoadSprites()
313 {
314 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
315
316 SwitchNewGRFBlitter();
317 ClearFontCache();
318 GfxInitSpriteMem();
319 LoadSpriteTables();
320 GfxInitPalettes();
321
322 UpdateCursorSize();
323 }
324
325 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
326 {
327 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
328 if (ret) {
329 IniGroup *metadata = ini->GetGroup("metadata");
330 IniItem *item;
331
332 fetch_metadata("palette");
333 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
334
335 /* Get optional blitter information. */
336 item = metadata->GetItem("blitter", false);
337 this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
338 }
339 return ret;
340 }
341
351 /* static */ MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
352 {
353 size_t size = 0;
354 FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
355 if (f == NULL) return MD5File::CR_NO_FILE;
356
357 size_t max = GRFGetSizeOfDataSection(f);
358
359 FioFCloseFile(f);
360
361 return file->CheckMD5(subdir, max);
362 }
363
364
374 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
375 {
376 size_t size;
377 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
378
379 if (f == NULL) return CR_NO_FILE;
380
381 size = min(size, max_size);
382
383 Md5 checksum;
384 uint8 buffer[1024];
385 uint8 digest[16];
386 size_t len;
387
388 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
389 size -= len;
390 checksum.Append(buffer, len);
391 }
392
393 FioFCloseFile(f);
394
395 checksum.Finish(digest);
396 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
397 }
398
400 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
401
403 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
404 /* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
405
406 template <class Tbase_set>
407 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
408 {
409 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
410
411 const Tbase_set *best = NULL;
412 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
413 /* Skip unusable sets */
414 if (c->GetNumMissing() != 0) continue;
415
416 if (best == NULL ||
417 (best->fallback && !c->fallback) ||
418 best->valid_files < c->valid_files ||
419 (best->valid_files == c->valid_files && (
420 (best->shortname == c->shortname && best->version < c->version) ||
421 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
422 best = c;
423 }
424 }
425
426 BaseMedia<Tbase_set>::used_set = best;
427 return BaseMedia<Tbase_set>::used_set != NULL;
428 }
429
430 template <class Tbase_set>
431 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
432 {
433 return ".obg"; // OpenTTD Base Graphics
434 }
435
436 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)