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