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
00151
00152
00153
00154 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00155 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00156 LoadGrfIndexed(
00157 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00158 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00159 i++
00160 );
00161 }
00162
00163
00164 InitializeUnicodeGlyphMap();
00165
00166
00167
00168
00169
00170
00171 GRFConfig *top = _grfconfig;
00172 GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00173
00174
00175
00176
00177
00178 switch (used_set->palette) {
00179 case PAL_DOS: master->palette |= GRFP_GRF_DOS; break;
00180 case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00181 default: break;
00182 }
00183 FillGRFDetails(master, false);
00184
00185 ClrBit(master->flags, GCF_INIT_ONLY);
00186 master->next = top;
00187 _grfconfig = master;
00188
00189 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00190
00191
00192 delete master;
00193 _grfconfig = top;
00194 }
00195
00196
00198 void GfxLoadSprites()
00199 {
00200 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00201
00202 GfxInitSpriteMem();
00203 LoadSpriteTables();
00204 GfxInitPalettes();
00205
00206 UpdateCursorSize();
00207 }
00208
00209 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00210 {
00211 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path, full_filename, false);
00212 if (ret) {
00213 IniGroup *metadata = ini->GetGroup("metadata");
00214 IniItem *item;
00215
00216 fetch_metadata("palette");
00217 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00218 }
00219 return ret;
00220 }
00221
00222
00231 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
00232 {
00233 size_t size;
00234 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00235
00236 if (f == NULL) return CR_NO_FILE;
00237
00238 Md5 checksum;
00239 uint8 buffer[1024];
00240 uint8 digest[16];
00241 size_t len;
00242
00243 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00244 size -= len;
00245 checksum.Append(buffer, len);
00246 }
00247
00248 FioFCloseFile(f);
00249
00250 checksum.Finish(digest);
00251 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00252 }
00253
00255 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00256
00258 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
00259 const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
00260
00261 template <class Tbase_set>
00262 bool BaseMedia<Tbase_set>::DetermineBestSet()
00263 {
00264 if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00265
00266 const Tbase_set *best = NULL;
00267 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00268
00269 if (c->GetNumMissing() != 0) continue;
00270
00271 if (best == NULL ||
00272 (best->fallback && !c->fallback) ||
00273 best->valid_files < c->valid_files ||
00274 (best->valid_files == c->valid_files && (
00275 (best->shortname == c->shortname && best->version < c->version) ||
00276 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00277 best = c;
00278 }
00279 }
00280
00281 BaseMedia<Tbase_set>::used_set = best;
00282 return BaseMedia<Tbase_set>::used_set != NULL;
00283 }
00284
00285 template <class Tbase_set>
00286 const char *BaseMedia<Tbase_set>::GetExtension()
00287 {
00288 return ".obg";
00289 }
00290
00291 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)