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