tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file gfxinit.cpp Initializing of the (GRF) graphics. */ belugas@6179: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" tron@2340: #include "gfxinit.h" tron@1349: #include "spritecache.h" truelight@0: #include "fileio.h" rubidium@7805: #include "fios.h" pasky@463: #include "newgrf.h" dominik@961: #include "md5.h" tron@2159: #include "variables.h" peter1138@5156: #include "fontcache.h" rubidium@8123: #include "gfx_func.h" rubidium@8213: #include "core/alloc_func.hpp" rubidium@8236: #include "core/bitmath_func.hpp" tron@4472: #include rubidium@8270: #include "settings_type.h" truelight@0: rubidium@8264: #include "table/sprites.h" rubidium@8264: rubidium@6248: struct MD5File { belugas@6179: const char * filename; ///< filename skidd13@8133: uint8 hash[16]; ///< md5 sum of the file rubidium@6248: }; dominik@961: rubidium@6248: struct FileList { rubidium@7882: MD5File basic[2]; ///< GRF files that always have to be loaded rubidium@7882: MD5File landscape[3]; ///< Landscape specific grf files rubidium@7882: MD5File sound; ///< Sound samples rubidium@7882: MD5File openttd; ///< GRF File with OTTD specific graphics rubidium@6248: }; truelight@0: dominik@961: #include "table/files.h" truelight@0: #include "table/landscape_sprite.h" truelight@0: celestar@2187: static const SpriteID * const _landscape_spriteindexes[] = { truelight@0: _landscape_spriteindexes_1, truelight@0: _landscape_spriteindexes_2, truelight@0: _landscape_spriteindexes_3, truelight@0: }; truelight@0: rubidium@7841: static uint LoadGrfFile(const char *filename, uint load_index, int file_index) truelight@0: { tron@2342: uint load_index_org = load_index; truelight@6908: uint sprite_id = 0; truelight@0: truelight@0: FioOpenFile(file_index, filename); darkvater@365: Darkvater@5380: DEBUG(sprite, 2, "Reading grf-file '%s'", filename); truelight@0: truelight@6908: while (LoadNextSprite(load_index, file_index, sprite_id)) { truelight@0: load_index++; truelight@6908: sprite_id++; celestar@2187: if (load_index >= MAX_SPRITES) { glx@9470: usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files."); truelight@0: } truelight@0: } Darkvater@5380: DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index); truelight@0: darkvater@365: return load_index - load_index_org; darkvater@365: } darkvater@365: tron@2342: rubidium@7772: void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl) rubidium@7772: { rubidium@7772: uint start; rubidium@7772: while ((start = *index_tbl++) != END) { rubidium@7772: uint end = *index_tbl++; rubidium@7772: peter1138@8432: do { peter1138@8432: bool b = LoadNextSprite(start, file_index, *sprite_id); peter1138@8432: assert(b); peter1138@8432: (*sprite_id)++; peter1138@8432: } while (++start <= end); rubidium@7772: } rubidium@7772: } rubidium@7772: tron@2342: static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index) darkvater@365: { truelight@6908: uint sprite_id = 0; dominik@614: darkvater@365: FioOpenFile(file_index, filename); darkvater@365: Darkvater@5380: DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); truelight@0: rubidium@7772: LoadSpritesIndexed(file_index, &sprite_id, index_tbl); truelight@184: } truelight@0: truelight@0: rubidium@7841: /** rubidium@7841: * Calculate and check the MD5 hash of the supplied filename. rubidium@7841: * @param file filename and expected MD5 hash for the given filename. rubidium@7841: * @return true if the checksum is correct. rubidium@7841: */ rubidium@7841: static bool FileMD5(const MD5File file) dominik@614: { truelight@7574: size_t size; truelight@7574: FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size); bjarni@5482: truelight@862: if (f != NULL) { skidd13@8133: Md5 checksum; skidd13@8133: uint8 buffer[1024]; skidd13@8133: uint8 digest[16]; tron@2028: size_t len; tron@2028: truelight@7574: while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { truelight@7574: size -= len; skidd13@8133: checksum.Append(buffer, len); truelight@7574: } dominik@961: truelight@7592: FioFCloseFile(f); tron@1019: skidd13@8133: checksum.Finish(digest); rubidium@7841: return memcmp(file.hash, digest, sizeof(file.hash)) == 0; dominik@961: } else { // file not found dominik@961: return false; tron@1019: } dominik@961: } dominik@961: rubidium@7841: /** rubidium@7841: * Determine the palette that has to be used. rubidium@7841: * - forced DOS palette via command line -> leave it that way rubidium@7841: * - all Windows files present -> Windows palette rubidium@7841: * - all DOS files present -> DOS palette rubidium@7841: * - no Windows files present and any DOS file present -> DOS palette rubidium@7841: * - otherwise -> Windows palette rubidium@7841: */ rubidium@7841: static void DeterminePalette() dominik@961: { rubidium@7841: if (_use_dos_palette) return; rubidium@7841: rubidium@7841: /* Count of files from the different versions. */ tron@1355: uint dos = 0; tron@1355: uint win = 0; dominik@961: rubidium@7841: for (uint i = 0; i < lengthof(files_dos.basic); i++) if (FioCheckFileExists(files_dos.basic[i].filename)) dos++; rubidium@7841: for (uint i = 0; i < lengthof(files_dos.landscape); i++) if (FioCheckFileExists(files_dos.landscape[i].filename)) dos++; dominik@961: rubidium@7841: for (uint i = 0; i < lengthof(files_win.basic); i++) if (FioCheckFileExists(files_win.basic[i].filename)) win++; rubidium@7841: for (uint i = 0; i < lengthof(files_win.landscape); i++) if (FioCheckFileExists(files_win.landscape[i].filename)) win++; peter1138@4934: rubidium@7841: if (win == 5) { dominik@961: _use_dos_palette = false; tron@1381: } else if (dos == 5 || (win == 0 && dos > 0)) { dominik@614: _use_dos_palette = true; tron@1380: } else { dominik@961: _use_dos_palette = false; dominik@614: } dominik@614: } dominik@614: rubidium@7841: /** rubidium@7841: * Checks whether the MD5 checksums of the files are correct. rubidium@7841: * rubidium@7841: * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption. rubidium@7841: */ rubidium@7841: void CheckExternalFiles() rubidium@7841: { rubidium@7841: DeterminePalette(); rubidium@7841: rubidium@7841: static const size_t ERROR_MESSAGE_LENGTH = 128; rubidium@7841: const FileList *files = _use_dos_palette ? &files_dos : &files_win; rubidium@7882: char error_msg[ERROR_MESSAGE_LENGTH * (lengthof(files->basic) + lengthof(files->landscape) + 3)]; rubidium@7841: error_msg[0] = '\0'; rubidium@7841: char *add_pos = error_msg; rubidium@7841: rubidium@7841: for (uint i = 0; i < lengthof(files->basic); i++) { rubidium@7841: if (!FileMD5(files->basic[i])) { rubidium@7841: add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! You can find '%s' on your Transport Tycoon Deluxe CD-ROM.\n", files->basic[i].filename, files->basic[i].filename); rubidium@7841: } rubidium@7841: } rubidium@7841: rubidium@7841: for (uint i = 0; i < lengthof(files->landscape); i++) { rubidium@7841: if (!FileMD5(files->landscape[i])) { rubidium@7841: add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! You can find '%s' on your Transport Tycoon Deluxe CD-ROM.\n", files->landscape[i].filename, files->landscape[i].filename); rubidium@7841: } rubidium@7841: } rubidium@7841: rubidium@7882: if (!FileMD5(files_win.sound) && !FileMD5(files_dos.sound)) { rubidium@7841: add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your 'sample.cat' file is corrupted or missing! You can find 'sample.cat' on your Transport Tycoon Deluxe CD-ROM.\n"); rubidium@7841: } rubidium@7841: rubidium@7882: if (!FileMD5(files->openttd)) { rubidium@7882: add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! The file was part of your installation.\n", files->openttd.filename); rubidium@7841: } rubidium@7841: rubidium@7841: if (add_pos != error_msg) ShowInfoF(error_msg); rubidium@7841: } rubidium@7841: tron@2342: rubidium@6247: static void LoadSpriteTables() truelight@0: { rubidium@7841: const FileList *files = _use_dos_palette ? &files_dos : &files_win; rubidium@7805: uint i = FIRST_GRF_SLOT; dominik@614: peter1138@8411: LoadGrfFile(files->basic[0].filename, 0, i++); tron@2353: rubidium@7841: /* rubidium@7841: * The second basic file always starts at the given location and does rubidium@7841: * contain a different amount of sprites depending on the "type"; DOS rubidium@7841: * has a few sprites less. However, we do not care about those missing rubidium@7841: * sprites as they are not shown anyway (logos in intro game). rubidium@7841: */ rubidium@7841: LoadGrfFile(files->basic[1].filename, 4793, i++); truelight@0: rubidium@7841: /* rubidium@7841: * Load additional sprites for climates other than temperate. rubidium@7841: * This overwrites some of the temperate sprites, such as foundations rubidium@7841: * and the ground sprites. rubidium@7841: */ rubidium@9413: if (_settings_game.game_creation.landscape != LT_TEMPERATE) { tron@2309: LoadGrfIndexed( rubidium@9413: files->landscape[_settings_game.game_creation.landscape - 1].filename, rubidium@9413: _landscape_spriteindexes[_settings_game.game_creation.landscape - 1], tron@2309: i++ tron@2309: ); tron@2309: } truelight@0: peter1138@5156: /* Initialize the unicode to sprite mapping table */ peter1138@5156: InitializeUnicodeGlyphMap(); peter1138@5156: rubidium@7882: /* rubidium@7882: * Load the base NewGRF with OTTD required graphics as first NewGRF. rubidium@7882: * However, we do not want it to show up in the list of used NewGRFs, rubidium@7882: * so we have to manually add it, and then remove it later. rubidium@7882: */ rubidium@7882: GRFConfig *top = _grfconfig; rubidium@7882: GRFConfig *master = CallocT(1); rubidium@7882: master->filename = strdup(files->openttd.filename); rubidium@7882: FillGRFDetails(master, false); skidd13@7929: ClrBit(master->flags, GCF_INIT_ONLY); rubidium@7882: master->next = top; rubidium@7882: _grfconfig = master; rubidium@7882: rubidium@7882: LoadNewGRF(SPR_NEWGRFS_BASE, i); rubidium@7882: rubidium@7882: /* Free and remove the top element. */ rubidium@7882: ClearGRFConfig(&master); rubidium@7882: _grfconfig = top; truelight@0: } truelight@0: truelight@0: rubidium@6247: void GfxLoadSprites() tron@1093: { rubidium@9413: DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape); truelight@0: peter1138@5151: GfxInitSpriteMem(); peter1138@5151: LoadSpriteTables(); peter1138@5151: GfxInitPalettes(); truelight@0: }