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 GfxInitSpriteMem();
00260 LoadSpriteTables();
00261 GfxInitPalettes();
00262
00263 UpdateCursorSize();
00264 }
00265
00266 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00267 {
00268 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
00269 if (ret) {
00270 IniGroup *metadata = ini->GetGroup("metadata");
00271 IniItem *item;
00272
00273 fetch_metadata("palette");
00274 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00275
00276
00277 item = metadata->GetItem("blitter", false);
00278 this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
00279 }
00280 return ret;
00281 }
00282
00292 MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
00293 {
00294 size_t size = 0;
00295 FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
00296 if (f == NULL) return MD5File::CR_NO_FILE;
00297
00298 size_t max = GRFGetSizeOfDataSection(f);
00299
00300 FioFCloseFile(f);
00301
00302 return file->CheckMD5(subdir, max);
00303 }
00304
00305
00315 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
00316 {
00317 size_t size;
00318 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00319
00320 if (f == NULL) return CR_NO_FILE;
00321
00322 size = min(size, max_size);
00323
00324 Md5 checksum;
00325 uint8 buffer[1024];
00326 uint8 digest[16];
00327 size_t len;
00328
00329 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00330 size -= len;
00331 checksum.Append(buffer, len);
00332 }
00333
00334 FioFCloseFile(f);
00335
00336 checksum.Finish(digest);
00337 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00338 }
00339
00341 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00342
00344 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
00345 const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
00346
00347 template <class Tbase_set>
00348 bool BaseMedia<Tbase_set>::DetermineBestSet()
00349 {
00350 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00351
00352 const Tbase_set *best = NULL;
00353 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00354
00355 if (c->GetNumMissing() != 0) continue;
00356
00357 if (best == NULL ||
00358 (best->fallback && !c->fallback) ||
00359 best->valid_files < c->valid_files ||
00360 (best->valid_files == c->valid_files && (
00361 (best->shortname == c->shortname && best->version < c->version) ||
00362 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00363 best = c;
00364 }
00365 }
00366
00367 BaseMedia<Tbase_set>::used_set = best;
00368 return BaseMedia<Tbase_set>::used_set != NULL;
00369 }
00370
00371 template <class Tbase_set>
00372 const char *BaseMedia<Tbase_set>::GetExtension()
00373 {
00374 return ".obg";
00375 }
00376
00377 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)