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 "blitter/factory.hpp"
00019 #include "video/video_driver.hpp"
00020
00021
00022 #define SET_TYPE "graphics"
00023 #include "base_media_func.h"
00024
00025 #include "table/sprites.h"
00026
00028 bool _palette_remap_grf[MAX_FILE_SLOTS];
00029
00030 #include "table/landscape_sprite.h"
00031
00033 static const SpriteID * const _landscape_spriteindexes[] = {
00034 _landscape_spriteindexes_arctic,
00035 _landscape_spriteindexes_tropic,
00036 _landscape_spriteindexes_toyland,
00037 };
00038
00046 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
00047 {
00048 uint load_index_org = load_index;
00049 uint sprite_id = 0;
00050
00051 FioOpenFile(file_index, filename, BASESET_DIR);
00052
00053 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
00054
00055 byte container_ver = GetGRFContainerVersion();
00056 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
00057 ReadGRFSpriteOffsets(container_ver);
00058 if (container_ver >= 2) {
00059
00060 byte compression = FioReadByte();
00061 if (compression != 0) usererror("Unsupported compression format");
00062 }
00063
00064 while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
00065 load_index++;
00066 sprite_id++;
00067 if (load_index >= MAX_SPRITES) {
00068 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00069 }
00070 }
00071 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00072
00073 return load_index - load_index_org;
00074 }
00075
00083 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00084 {
00085 uint start;
00086 uint sprite_id = 0;
00087
00088 FioOpenFile(file_index, filename, BASESET_DIR);
00089
00090 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00091
00092 byte container_ver = GetGRFContainerVersion();
00093 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
00094 ReadGRFSpriteOffsets(container_ver);
00095 if (container_ver >= 2) {
00096
00097 byte compression = FioReadByte();
00098 if (compression != 0) usererror("Unsupported compression format");
00099 }
00100
00101 while ((start = *index_tbl++) != END) {
00102 uint end = *index_tbl++;
00103
00104 do {
00105 bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
00106 assert(b);
00107 sprite_id++;
00108 } while (++start <= end);
00109 }
00110 }
00111
00117 void CheckExternalFiles()
00118 {
00119 if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
00120
00121 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00122
00123 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
00124
00125 static const size_t ERROR_MESSAGE_LENGTH = 256;
00126 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00127
00128
00129
00130
00131 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
00132 error_msg[0] = '\0';
00133 char *add_pos = error_msg;
00134 const char *last = lastof(error_msg);
00135
00136 if (used_set->GetNumInvalid() != 0) {
00137
00138 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);
00139 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
00140 MD5File::ChecksumResult res = GraphicsSet::CheckMD5(&used_set->files[i], BASESET_DIR);
00141 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);
00142 }
00143 add_pos += seprintf(add_pos, last, "\n");
00144 }
00145
00146 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
00147 if (sounds_set->GetNumInvalid() != 0) {
00148 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);
00149
00150 assert_compile(SoundsSet::NUM_FILES == 1);
00151
00152
00153 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);
00154 }
00155
00156 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00157 }
00158
00160 static void LoadSpriteTables()
00161 {
00162 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00163 uint i = FIRST_GRF_SLOT;
00164 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00165
00166 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00167 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
00168
00169
00170
00171
00172
00173
00174
00175 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00176 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
00177
00178
00179
00180
00181
00182
00183 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00184 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00185 LoadGrfFileIndexed(
00186 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00187 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00188 i++
00189 );
00190 }
00191
00192
00193 InitializeUnicodeGlyphMap();
00194
00195
00196
00197
00198
00199
00200 GRFConfig *top = _grfconfig;
00201 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00202
00203
00204
00205
00206
00207 switch (used_set->palette) {
00208 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
00209 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00210 default: break;
00211 }
00212 FillGRFDetails(master, false, BASESET_DIR);
00213
00214 ClrBit(master->flags, GCF_INIT_ONLY);
00215 master->next = top;
00216 _grfconfig = master;
00217
00218 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00219
00220
00221 delete master;
00222 _grfconfig = top;
00223 }
00224
00225
00229 static void SwitchNewGRFBlitter()
00230 {
00231
00232 bool is_32bpp = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP;
00233
00234
00235 for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00236 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
00237
00238 if (c->palette & GRFP_BLT_32BPP) is_32bpp = true;
00239 }
00240
00241
00242 if (_blitter_autodetected && is_32bpp && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() != 0 && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() < 16) {
00243 const char *cur_blitter = BlitterFactoryBase::GetCurrentBlitter()->GetName();
00244 if (BlitterFactoryBase::SelectBlitter("32bpp-anim") != NULL) {
00245 if (!_video_driver->AfterBlitterChange()) {
00246
00247 if (BlitterFactoryBase::SelectBlitter(cur_blitter) == NULL || !_video_driver->AfterBlitterChange()) usererror("Failed to reinitialize video driver for 32 bpp blitter. Specify a fixed blitter in the config");
00248 }
00249 }
00250 }
00251 }
00252
00254 void GfxLoadSprites()
00255 {
00256 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00257
00258 SwitchNewGRFBlitter();
00259 ClearFontCache();
00260 GfxInitSpriteMem();
00261 LoadSpriteTables();
00262 GfxInitPalettes();
00263
00264 UpdateCursorSize();
00265 }
00266
00267 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00268 {
00269 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
00270 if (ret) {
00271 IniGroup *metadata = ini->GetGroup("metadata");
00272 IniItem *item;
00273
00274 fetch_metadata("palette");
00275 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00276
00277
00278 item = metadata->GetItem("blitter", false);
00279 this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
00280 }
00281 return ret;
00282 }
00283
00293 MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
00294 {
00295 size_t size = 0;
00296 FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
00297 if (f == NULL) return MD5File::CR_NO_FILE;
00298
00299 size_t max = GRFGetSizeOfDataSection(f);
00300
00301 FioFCloseFile(f);
00302
00303 return file->CheckMD5(subdir, max);
00304 }
00305
00306
00316 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
00317 {
00318 size_t size;
00319 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00320
00321 if (f == NULL) return CR_NO_FILE;
00322
00323 size = min(size, max_size);
00324
00325 Md5 checksum;
00326 uint8 buffer[1024];
00327 uint8 digest[16];
00328 size_t len;
00329
00330 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00331 size -= len;
00332 checksum.Append(buffer, len);
00333 }
00334
00335 FioFCloseFile(f);
00336
00337 checksum.Finish(digest);
00338 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00339 }
00340
00342 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00343
00345 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
00346 const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
00347
00348 template <class Tbase_set>
00349 bool BaseMedia<Tbase_set>::DetermineBestSet()
00350 {
00351 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00352
00353 const Tbase_set *best = NULL;
00354 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00355
00356 if (c->GetNumMissing() != 0) continue;
00357
00358 if (best == NULL ||
00359 (best->fallback && !c->fallback) ||
00360 best->valid_files < c->valid_files ||
00361 (best->valid_files == c->valid_files && (
00362 (best->shortname == c->shortname && best->version < c->version) ||
00363 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00364 best = c;
00365 }
00366 }
00367
00368 BaseMedia<Tbase_set>::used_set = best;
00369 return BaseMedia<Tbase_set>::used_set != NULL;
00370 }
00371
00372 template <class Tbase_set>
00373 const char *BaseMedia<Tbase_set>::GetExtension()
00374 {
00375 return ".obg";
00376 }
00377
00378 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)