00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "fios.h"
00014 #include "newgrf.h"
00015 #include "3rdparty/md5/md5.h"
00016 #include "fontcache.h"
00017 #include "gfx_func.h"
00018 #include "transparency.h"
00019 #include "blitter/factory.hpp"
00020 #include "video/video_driver.hpp"
00021
00022
00023 #define SET_TYPE "graphics"
00024 #include "base_media_func.h"
00025
00026 #include "table/sprites.h"
00027
00029 bool _palette_remap_grf[MAX_FILE_SLOTS];
00030
00031 #include "table/landscape_sprite.h"
00032
00034 static const SpriteID * const _landscape_spriteindexes[] = {
00035 _landscape_spriteindexes_arctic,
00036 _landscape_spriteindexes_tropic,
00037 _landscape_spriteindexes_toyland,
00038 };
00039
00047 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
00048 {
00049 uint load_index_org = load_index;
00050 uint sprite_id = 0;
00051
00052 FioOpenFile(file_index, filename, BASESET_DIR);
00053
00054 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
00055
00056 byte container_ver = GetGRFContainerVersion();
00057 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
00058 ReadGRFSpriteOffsets(container_ver);
00059 if (container_ver >= 2) {
00060
00061 byte compression = FioReadByte();
00062 if (compression != 0) usererror("Unsupported compression format");
00063 }
00064
00065 while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
00066 load_index++;
00067 sprite_id++;
00068 if (load_index >= MAX_SPRITES) {
00069 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00070 }
00071 }
00072 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00073
00074 return load_index - load_index_org;
00075 }
00076
00084 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00085 {
00086 uint start;
00087 uint sprite_id = 0;
00088
00089 FioOpenFile(file_index, filename, BASESET_DIR);
00090
00091 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00092
00093 byte container_ver = GetGRFContainerVersion();
00094 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
00095 ReadGRFSpriteOffsets(container_ver);
00096 if (container_ver >= 2) {
00097
00098 byte compression = FioReadByte();
00099 if (compression != 0) usererror("Unsupported compression format");
00100 }
00101
00102 while ((start = *index_tbl++) != END) {
00103 uint end = *index_tbl++;
00104
00105 do {
00106 bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
00107 assert(b);
00108 sprite_id++;
00109 } while (++start <= end);
00110 }
00111 }
00112
00118 void CheckExternalFiles()
00119 {
00120 if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
00121
00122 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00123
00124 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
00125
00126 static const size_t ERROR_MESSAGE_LENGTH = 256;
00127 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00128
00129
00130
00131
00132 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
00133 error_msg[0] = '\0';
00134 char *add_pos = error_msg;
00135 const char *last = lastof(error_msg);
00136
00137 if (used_set->GetNumInvalid() != 0) {
00138
00139 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);
00140 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
00141 MD5File::ChecksumResult res = GraphicsSet::CheckMD5(&used_set->files[i], BASESET_DIR);
00142 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);
00143 }
00144 add_pos += seprintf(add_pos, last, "\n");
00145 }
00146
00147 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
00148 if (sounds_set->GetNumInvalid() != 0) {
00149 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);
00150
00151 assert_compile(SoundsSet::NUM_FILES == 1);
00152
00153
00154 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);
00155 }
00156
00157 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00158 }
00159
00161 static void LoadSpriteTables()
00162 {
00163 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00164 uint i = FIRST_GRF_SLOT;
00165 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00166
00167 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00168 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
00169
00170
00171
00172
00173
00174
00175
00176 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00177 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
00178
00179
00180
00181
00182
00183
00184 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00185 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00186 LoadGrfFileIndexed(
00187 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00188 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00189 i++
00190 );
00191 }
00192
00193
00194 InitializeUnicodeGlyphMap();
00195
00196
00197
00198
00199
00200
00201 GRFConfig *top = _grfconfig;
00202 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00203
00204
00205
00206
00207
00208 switch (used_set->palette) {
00209 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
00210 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00211 default: break;
00212 }
00213 FillGRFDetails(master, false, BASESET_DIR);
00214
00215 ClrBit(master->flags, GCF_INIT_ONLY);
00216 master->next = top;
00217 _grfconfig = master;
00218
00219 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00220
00221
00222 delete master;
00223 _grfconfig = top;
00224 }
00225
00226
00231 static bool SwitchNewGRFBlitter()
00232 {
00233
00234 if (!_blitter_autodetected) return false;
00235
00236
00237 if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return false;
00238
00239
00240 uint depth_wanted_by_base = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP ? 32 : 8;
00241 uint depth_wanted_by_grf = 8;
00242 for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00243 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
00244 if (c->palette & GRFP_BLT_32BPP) depth_wanted_by_grf = 32;
00245 }
00246
00247
00248 struct {
00249 const char *name;
00250 uint animation;
00251 uint min_base_depth, max_base_depth, min_grf_depth, max_grf_depth;
00252 } replacement_blitters[] = {
00253 #ifdef WITH_SSE
00254 { "32bpp-sse4", 0, 32, 32, 8, 32 },
00255 { "32bpp-ssse3", 0, 32, 32, 8, 32 },
00256 { "32bpp-sse2", 0, 32, 32, 8, 32 },
00257 { "32bpp-sse4-anim", 1, 32, 32, 8, 32 },
00258 #endif
00259 { "8bpp-optimized", 2, 8, 8, 8, 8 },
00260 { "32bpp-optimized", 0, 8, 32, 8, 32 },
00261 { "32bpp-anim", 1, 8, 32, 8, 32 },
00262 };
00263
00264 const bool animation_wanted = HasBit(_display_opt, DO_FULL_ANIMATION);
00265 const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
00266
00267 for (uint i = 0; i < lengthof(replacement_blitters); i++) {
00268 if (animation_wanted && (replacement_blitters[i].animation == 0)) continue;
00269 if (!animation_wanted && (replacement_blitters[i].animation == 1)) continue;
00270
00271 if (!IsInsideMM(depth_wanted_by_base, replacement_blitters[i].min_base_depth, replacement_blitters[i].max_base_depth + 1)) continue;
00272 if (!IsInsideMM(depth_wanted_by_grf, replacement_blitters[i].min_grf_depth, replacement_blitters[i].max_grf_depth + 1)) continue;
00273 const char *repl_blitter = replacement_blitters[i].name;
00274
00275 if (strcmp(repl_blitter, cur_blitter) == 0) return false;
00276 if (BlitterFactory::GetBlitterFactory(repl_blitter) == NULL) continue;
00277
00278 DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
00279 Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
00280 if (new_blitter == NULL) NOT_REACHED();
00281 DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter);
00282 break;
00283 }
00284
00285 if (!VideoDriver::GetInstance()->AfterBlitterChange()) {
00286
00287 if (BlitterFactory::SelectBlitter(cur_blitter) == NULL || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
00288 }
00289
00290 return true;
00291 }
00292
00294 void CheckBlitter()
00295 {
00296 if (!SwitchNewGRFBlitter()) return;
00297
00298 ClearFontCache();
00299 GfxClearSpriteCache();
00300 }
00301
00303 void GfxLoadSprites()
00304 {
00305 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00306
00307 SwitchNewGRFBlitter();
00308 ClearFontCache();
00309 GfxInitSpriteMem();
00310 LoadSpriteTables();
00311 GfxInitPalettes();
00312
00313 UpdateCursorSize();
00314 }
00315
00316 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00317 {
00318 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
00319 if (ret) {
00320 IniGroup *metadata = ini->GetGroup("metadata");
00321 IniItem *item;
00322
00323 fetch_metadata("palette");
00324 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00325
00326
00327 item = metadata->GetItem("blitter", false);
00328 this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
00329 }
00330 return ret;
00331 }
00332
00342 MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
00343 {
00344 size_t size = 0;
00345 FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
00346 if (f == NULL) return MD5File::CR_NO_FILE;
00347
00348 size_t max = GRFGetSizeOfDataSection(f);
00349
00350 FioFCloseFile(f);
00351
00352 return file->CheckMD5(subdir, max);
00353 }
00354
00355
00365 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
00366 {
00367 size_t size;
00368 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00369
00370 if (f == NULL) return CR_NO_FILE;
00371
00372 size = min(size, max_size);
00373
00374 Md5 checksum;
00375 uint8 buffer[1024];
00376 uint8 digest[16];
00377 size_t len;
00378
00379 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00380 size -= len;
00381 checksum.Append(buffer, len);
00382 }
00383
00384 FioFCloseFile(f);
00385
00386 checksum.Finish(digest);
00387 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00388 }
00389
00391 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00392
00394 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
00395 const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
00396
00397 template <class Tbase_set>
00398 bool BaseMedia<Tbase_set>::DetermineBestSet()
00399 {
00400 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00401
00402 const Tbase_set *best = NULL;
00403 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00404
00405 if (c->GetNumMissing() != 0) continue;
00406
00407 if (best == NULL ||
00408 (best->fallback && !c->fallback) ||
00409 best->valid_files < c->valid_files ||
00410 (best->valid_files == c->valid_files && (
00411 (best->shortname == c->shortname && best->version < c->version) ||
00412 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00413 best = c;
00414 }
00415 }
00416
00417 BaseMedia<Tbase_set>::used_set = best;
00418 return BaseMedia<Tbase_set>::used_set != NULL;
00419 }
00420
00421 template <class Tbase_set>
00422 const char *BaseMedia<Tbase_set>::GetExtension()
00423 {
00424 return ".obg";
00425 }
00426
00427 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)