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