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
00019
00020 #define SET_TYPE "graphics"
00021 #include "base_media_func.h"
00022
00023 #include "table/sprites.h"
00024
00026 bool _palette_remap_grf[MAX_FILE_SLOTS];
00027
00028 #include "table/landscape_sprite.h"
00029
00030 static const SpriteID * const _landscape_spriteindexes[] = {
00031 _landscape_spriteindexes_1,
00032 _landscape_spriteindexes_2,
00033 _landscape_spriteindexes_3,
00034 };
00035
00036 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
00037 {
00038 uint load_index_org = load_index;
00039 uint sprite_id = 0;
00040
00041 FioOpenFile(file_index, filename);
00042
00043 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
00044
00045 while (LoadNextSprite(load_index, file_index, sprite_id)) {
00046 load_index++;
00047 sprite_id++;
00048 if (load_index >= MAX_SPRITES) {
00049 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00050 }
00051 }
00052 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00053
00054 return load_index - load_index_org;
00055 }
00056
00057
00058 static void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl)
00059 {
00060 uint start;
00061 while ((start = *index_tbl++) != END) {
00062 uint end = *index_tbl++;
00063
00064 do {
00065 bool b = LoadNextSprite(start, file_index, *sprite_id);
00066 assert(b);
00067 (*sprite_id)++;
00068 } while (++start <= end);
00069 }
00070 }
00071
00072 static void LoadGrfIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00073 {
00074 uint sprite_id = 0;
00075
00076 FioOpenFile(file_index, filename);
00077
00078 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00079
00080 LoadSpritesIndexed(file_index, &sprite_id, index_tbl);
00081 }
00082
00088 void CheckExternalFiles()
00089 {
00090 if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
00091
00092 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00093
00094 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
00095
00096 static const size_t ERROR_MESSAGE_LENGTH = 256;
00097 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00098
00099
00100
00101
00102 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
00103 error_msg[0] = '\0';
00104 char *add_pos = error_msg;
00105 const char *last = lastof(error_msg);
00106
00107 if (used_set->GetNumInvalid() != 0) {
00108
00109 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);
00110 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
00111 MD5File::ChecksumResult res = used_set->files[i].CheckMD5(DATA_DIR);
00112 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);
00113 }
00114 add_pos += seprintf(add_pos, last, "\n");
00115 }
00116
00117 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
00118 if (sounds_set->GetNumInvalid() != 0) {
00119 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);
00120
00121 assert_compile(SoundsSet::NUM_FILES == 1);
00122
00123
00124 add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5(DATA_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
00125 }
00126
00127 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00128 }
00129
00131 static void LoadSpriteTables()
00132 {
00133 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00134 uint i = FIRST_GRF_SLOT;
00135 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00136
00137 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00138 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
00139
00140
00141
00142
00143
00144
00145
00146 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00147 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
00148
00149
00150 LoadGrfFile("flat_blacktiles.grf", SPR_FLAT_BLACKTILES, i++);
00151
00152
00153
00154
00155
00156
00157 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00158 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00159 LoadGrfIndexed(
00160 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00161 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00162 i++
00163 );
00164 }
00165
00166
00167 InitializeUnicodeGlyphMap();
00168
00169
00170 LoadGrfFile("copypaste.grf", SPR_COPYPASTE_BASE, i++);
00171
00172
00173 LoadGrfFile("trafficlights.grf", SPR_TRAFFICLIGHTS_BASE, i++);
00174
00175
00176 LoadGrfFile("speedsignals.grf", SPR_SPEEDSIGNAL_BASE, i++);
00177
00178
00179 LoadGrfFile("progsignals.grf", SPR_PROGSIGNAL_BASE, i++);
00180
00181
00182
00183
00184
00185 GRFConfig *top = _grfconfig;
00186 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00187
00188
00189
00190
00191
00192 switch (used_set->palette) {
00193 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
00194 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00195 default: break;
00196 }
00197 FillGRFDetails(master, false);
00198
00199 ClrBit(master->flags, GCF_INIT_ONLY);
00200 master->next = top;
00201 _grfconfig = master;
00202
00203 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00204
00205
00206 delete master;
00207 _grfconfig = top;
00208 }
00209
00210
00212 void GfxLoadSprites()
00213 {
00214 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00215
00216 GfxInitSpriteMem();
00217 LoadSpriteTables();
00218 GfxInitPalettes();
00219
00220 UpdateCursorSize();
00221 }
00222
00223 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00224 {
00225 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path, full_filename, false);
00226 if (ret) {
00227 IniGroup *metadata = ini->GetGroup("metadata");
00228 IniItem *item;
00229
00230 fetch_metadata("palette");
00231 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00232 }
00233 return ret;
00234 }
00235
00236
00245 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
00246 {
00247 size_t size;
00248 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00249
00250 if (f == NULL) return CR_NO_FILE;
00251
00252 Md5 checksum;
00253 uint8 buffer[1024];
00254 uint8 digest[16];
00255 size_t len;
00256
00257 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00258 size -= len;
00259 checksum.Append(buffer, len);
00260 }
00261
00262 FioFCloseFile(f);
00263
00264 checksum.Finish(digest);
00265 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00266 }
00267
00269 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00270
00272 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
00273 const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
00274
00275 template <class Tbase_set>
00276 bool BaseMedia<Tbase_set>::DetermineBestSet()
00277 {
00278 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00279
00280 const Tbase_set *best = NULL;
00281 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00282
00283 if (c->GetNumMissing() != 0) continue;
00284
00285 if (best == NULL ||
00286 (best->fallback && !c->fallback) ||
00287 best->valid_files < c->valid_files ||
00288 (best->valid_files == c->valid_files && (
00289 (best->shortname == c->shortname && best->version < c->version) ||
00290 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00291 best = c;
00292 }
00293 }
00294
00295 BaseMedia<Tbase_set>::used_set = best;
00296 return BaseMedia<Tbase_set>::used_set != NULL;
00297 }
00298
00299 template <class Tbase_set>
00300 const char *BaseMedia<Tbase_set>::GetExtension()
00301 {
00302 return ".obg";
00303 }
00304
00305 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)