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 #include "table/strings.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 while (LoadNextSprite(load_index, file_index, sprite_id)) {
00057 load_index++;
00058 sprite_id++;
00059 if (load_index >= MAX_SPRITES) {
00060 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00061 }
00062 }
00063 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00064
00065 return load_index - load_index_org;
00066 }
00067
00075 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00076 {
00077 uint start;
00078 uint sprite_id = 0;
00079
00080 FioOpenFile(file_index, filename, BASESET_DIR);
00081
00082 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00083
00084 while ((start = *index_tbl++) != END) {
00085 uint end = *index_tbl++;
00086
00087 do {
00088 bool b = LoadNextSprite(start, file_index, sprite_id);
00089 assert(b);
00090 sprite_id++;
00091 } while (++start <= end);
00092 }
00093 }
00094
00100 void CheckExternalFiles()
00101 {
00102 if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
00103
00104 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00105
00106 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
00107
00108 static const size_t ERROR_MESSAGE_LENGTH = 256;
00109 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00110
00111
00112
00113
00114 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
00115 error_msg[0] = '\0';
00116 char *add_pos = error_msg;
00117 const char *last = lastof(error_msg);
00118
00119 if (used_set->GetNumInvalid() != 0) {
00120
00121 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);
00122 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
00123 MD5File::ChecksumResult res = used_set->files[i].CheckMD5(BASESET_DIR);
00124 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);
00125 }
00126 add_pos += seprintf(add_pos, last, "\n");
00127 }
00128
00129 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
00130 if (sounds_set->GetNumInvalid() != 0) {
00131 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);
00132
00133 assert_compile(SoundsSet::NUM_FILES == 1);
00134
00135
00136 add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5(BASESET_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
00137 }
00138
00139 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00140 }
00141
00143 static void LoadSpriteTables()
00144 {
00145 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00146 uint i = FIRST_GRF_SLOT;
00147 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00148
00149 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00150 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
00151
00152
00153
00154
00155
00156
00157
00158 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00159 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
00160
00161
00162
00163
00164
00165
00166 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00167 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00168 LoadGrfFileIndexed(
00169 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00170 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00171 i++
00172 );
00173 }
00174
00175
00176 InitializeUnicodeGlyphMap();
00177
00178
00179
00180
00181
00182
00183 GRFConfig *top = _grfconfig;
00184 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00185
00186
00187
00188
00189
00190 switch (used_set->palette) {
00191 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
00192 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00193 default: break;
00194 }
00195 FillGRFDetails(master, false, BASESET_DIR);
00196
00197 ClrBit(master->flags, GCF_INIT_ONLY);
00198 master->next = top;
00199 _grfconfig = master;
00200
00201 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00202
00203
00204 delete master;
00205 _grfconfig = top;
00206 }
00207
00208
00212 static void SwitchNewGRFBlitter()
00213 {
00214
00215 bool is_32bpp = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP;
00216
00217
00218 for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00219 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
00220
00221 if (c->palette & GRFP_BLT_32BPP) is_32bpp = true;
00222 }
00223
00224
00225 if (_blitter_autodetected && is_32bpp && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() != 0 && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() < 16) {
00226 const char *cur_blitter = BlitterFactoryBase::GetCurrentBlitter()->GetName();
00227 if (BlitterFactoryBase::SelectBlitter("32bpp-anim") != NULL) {
00228 if (!_video_driver->AfterBlitterChange()) {
00229
00230 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");
00231 }
00232 }
00233 }
00234 }
00235
00237 void GfxLoadSprites()
00238 {
00239 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00240
00241 SwitchNewGRFBlitter();
00242 GfxInitSpriteMem();
00243 LoadSpriteTables();
00244 GfxInitPalettes();
00245
00246 UpdateCursorSize();
00247 }
00248
00249 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00250 {
00251 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
00252 if (ret) {
00253 IniGroup *metadata = ini->GetGroup("metadata");
00254 IniItem *item;
00255
00256 fetch_metadata("palette");
00257 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00258
00259
00260 item = metadata->GetItem("blitter", false);
00261 this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
00262 }
00263 return ret;
00264 }
00265
00266
00275 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
00276 {
00277 size_t size;
00278 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00279
00280 if (f == NULL) return CR_NO_FILE;
00281
00282 Md5 checksum;
00283 uint8 buffer[1024];
00284 uint8 digest[16];
00285 size_t len;
00286
00287 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00288 size -= len;
00289 checksum.Append(buffer, len);
00290 }
00291
00292 FioFCloseFile(f);
00293
00294 checksum.Finish(digest);
00295 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00296 }
00297
00299 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00300
00302 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
00303 const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
00304
00305 template <class Tbase_set>
00306 bool BaseMedia<Tbase_set>::DetermineBestSet()
00307 {
00308 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00309
00310 const Tbase_set *best = NULL;
00311 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00312
00313 if (c->GetNumMissing() != 0) continue;
00314
00315 if (best == NULL ||
00316 (best->fallback && !c->fallback) ||
00317 best->valid_files < c->valid_files ||
00318 (best->valid_files == c->valid_files && (
00319 (best->shortname == c->shortname && best->version < c->version) ||
00320 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00321 best = c;
00322 }
00323 }
00324
00325 BaseMedia<Tbase_set>::used_set = best;
00326 return BaseMedia<Tbase_set>::used_set != NULL;
00327 }
00328
00329 template <class Tbase_set>
00330 const char *BaseMedia<Tbase_set>::GetExtension()
00331 {
00332 return ".obg";
00333 }
00334
00335 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)