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