OpenTTD
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 
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 
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++) {
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);
192  i++
193  );
194  }
195 
196  /* Initialize the unicode to sprite mapping table */
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 
303 {
304  if (!SwitchNewGRFBlitter()) return;
305 
306  ClearFontCache();
309 }
310 
313 {
314  DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
315 
317  ClearFontCache();
318  GfxInitSpriteMem();
320  GfxInitPalettes();
321 
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 
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 
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>
405 
406 template <class Tbase_set>
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 
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