tron@2186: /* $Id$ */ tron@2186: belugas@6505: /** @file gfxinit.cpp */ belugas@6505: 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@8301: #include "fios.h" pasky@463: #include "newgrf.h" dominik@961: #include "md5.h" tron@2159: #include "variables.h" peter1138@5156: #include "fontcache.h" rubidium@8619: #include "gfx_func.h" rubidium@8709: #include "core/alloc_func.hpp" rubidium@8732: #include "core/bitmath_func.hpp" tron@4472: #include rubidium@8766: #include "settings_type.h" truelight@0: rubidium@8760: #include "table/sprites.h" rubidium@8760: rubidium@6574: struct MD5File { belugas@6505: const char * filename; ///< filename skidd13@8629: uint8 hash[16]; ///< md5 sum of the file rubidium@6574: }; dominik@961: rubidium@6574: struct FileList { rubidium@8378: MD5File basic[2]; ///< GRF files that always have to be loaded rubidium@8378: MD5File landscape[3]; ///< Landscape specific grf files rubidium@8378: MD5File sound; ///< Sound samples rubidium@8378: MD5File openttd; ///< GRF File with OTTD specific graphics rubidium@6574: }; 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@8337: static uint LoadGrfFile(const char *filename, uint load_index, int file_index) truelight@0: { tron@2342: uint load_index_org = load_index; truelight@7404: uint sprite_id = 0; truelight@0: truelight@0: FioOpenFile(file_index, filename); darkvater@365: Darkvater@5568: DEBUG(sprite, 2, "Reading grf-file '%s'", filename); truelight@0: truelight@7404: while (LoadNextSprite(load_index, file_index, sprite_id)) { truelight@0: load_index++; truelight@7404: sprite_id++; celestar@2187: if (load_index >= MAX_SPRITES) { celestar@2187: error("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files."); truelight@0: } truelight@0: } Darkvater@5568: 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@8268: void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl) rubidium@8268: { rubidium@8268: uint start; rubidium@8268: while ((start = *index_tbl++) != END) { rubidium@8268: uint end = *index_tbl++; rubidium@8268: peter1138@8928: do { peter1138@8928: #ifdef NDEBUG peter1138@8928: LoadNextSprite(start, file_index, *sprite_id); peter1138@8928: #else peter1138@8928: bool b = LoadNextSprite(start, file_index, *sprite_id); peter1138@8928: assert(b); peter1138@8928: #endif peter1138@8928: (*sprite_id)++; peter1138@8928: } while (++start <= end); rubidium@8268: } rubidium@8268: } rubidium@8268: tron@2342: static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index) darkvater@365: { truelight@7404: uint sprite_id = 0; dominik@614: darkvater@365: FioOpenFile(file_index, filename); darkvater@365: Darkvater@5568: DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); truelight@0: rubidium@8268: LoadSpritesIndexed(file_index, &sprite_id, index_tbl); truelight@184: } truelight@0: truelight@0: rubidium@8337: /** rubidium@8337: * Calculate and check the MD5 hash of the supplied filename. rubidium@8337: * @param file filename and expected MD5 hash for the given filename. rubidium@8337: * @return true if the checksum is correct. rubidium@8337: */ rubidium@8337: static bool FileMD5(const MD5File file) dominik@614: { truelight@8070: size_t size; truelight@8070: FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size); bjarni@5733: truelight@862: if (f != NULL) { skidd13@8629: Md5 checksum; skidd13@8629: uint8 buffer[1024]; skidd13@8629: uint8 digest[16]; tron@2028: size_t len; tron@2028: truelight@8070: while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { truelight@8070: size -= len; skidd13@8629: checksum.Append(buffer, len); truelight@8070: } dominik@961: truelight@8088: FioFCloseFile(f); tron@1019: skidd13@8629: checksum.Finish(digest); rubidium@8337: 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@8337: /** rubidium@8337: * Determine the palette that has to be used. rubidium@8337: * - forced DOS palette via command line -> leave it that way rubidium@8337: * - all Windows files present -> Windows palette rubidium@8337: * - all DOS files present -> DOS palette rubidium@8337: * - no Windows files present and any DOS file present -> DOS palette rubidium@8337: * - otherwise -> Windows palette rubidium@8337: */ rubidium@8337: static void DeterminePalette() dominik@961: { rubidium@8337: if (_use_dos_palette) return; rubidium@8337: rubidium@8337: /* Count of files from the different versions. */ tron@1355: uint dos = 0; tron@1355: uint win = 0; dominik@961: rubidium@8337: for (uint i = 0; i < lengthof(files_dos.basic); i++) if (FioCheckFileExists(files_dos.basic[i].filename)) dos++; rubidium@8337: for (uint i = 0; i < lengthof(files_dos.landscape); i++) if (FioCheckFileExists(files_dos.landscape[i].filename)) dos++; dominik@961: rubidium@8337: for (uint i = 0; i < lengthof(files_win.basic); i++) if (FioCheckFileExists(files_win.basic[i].filename)) win++; rubidium@8337: for (uint i = 0; i < lengthof(files_win.landscape); i++) if (FioCheckFileExists(files_win.landscape[i].filename)) win++; peter1138@4934: rubidium@8337: 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@8337: /** rubidium@8337: * Checks whether the MD5 checksums of the files are correct. rubidium@8337: * rubidium@8337: * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption. rubidium@8337: */ rubidium@8337: void CheckExternalFiles() rubidium@8337: { rubidium@8337: DeterminePalette(); rubidium@8337: rubidium@8337: static const size_t ERROR_MESSAGE_LENGTH = 128; rubidium@8337: const FileList *files = _use_dos_palette ? &files_dos : &files_win; rubidium@8378: char error_msg[ERROR_MESSAGE_LENGTH * (lengthof(files->basic) + lengthof(files->landscape) + 3)]; rubidium@8337: error_msg[0] = '\0'; rubidium@8337: char *add_pos = error_msg; rubidium@8337: rubidium@8337: for (uint i = 0; i < lengthof(files->basic); i++) { rubidium@8337: if (!FileMD5(files->basic[i])) { rubidium@8337: 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@8337: } rubidium@8337: } rubidium@8337: rubidium@8337: for (uint i = 0; i < lengthof(files->landscape); i++) { rubidium@8337: if (!FileMD5(files->landscape[i])) { rubidium@8337: 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@8337: } rubidium@8337: } rubidium@8337: rubidium@8378: if (!FileMD5(files_win.sound) && !FileMD5(files_dos.sound)) { rubidium@8337: 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@8337: } rubidium@8337: rubidium@8378: if (!FileMD5(files->openttd)) { rubidium@8378: 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@8337: } rubidium@8337: rubidium@8337: if (add_pos != error_msg) ShowInfoF(error_msg); rubidium@8337: } rubidium@8337: tron@2342: rubidium@6573: static void LoadSpriteTables() truelight@0: { rubidium@8337: const FileList *files = _use_dos_palette ? &files_dos : &files_win; rubidium@8301: uint i = FIRST_GRF_SLOT; dominik@614: peter1138@8907: LoadGrfFile(files->basic[0].filename, 0, i++); tron@2353: rubidium@8337: /* rubidium@8337: * The second basic file always starts at the given location and does rubidium@8337: * contain a different amount of sprites depending on the "type"; DOS rubidium@8337: * has a few sprites less. However, we do not care about those missing rubidium@8337: * sprites as they are not shown anyway (logos in intro game). rubidium@8337: */ rubidium@8337: LoadGrfFile(files->basic[1].filename, 4793, i++); truelight@0: rubidium@8337: /* rubidium@8337: * Load additional sprites for climates other than temperate. rubidium@8337: * This overwrites some of the temperate sprites, such as foundations rubidium@8337: * and the ground sprites. rubidium@8337: */ belugas@6683: if (_opt.landscape != LT_TEMPERATE) { tron@2309: LoadGrfIndexed( peter1138@5151: files->landscape[_opt.landscape - 1].filename, peter1138@5151: _landscape_spriteindexes[_opt.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@8378: /* rubidium@8378: * Load the base NewGRF with OTTD required graphics as first NewGRF. rubidium@8378: * However, we do not want it to show up in the list of used NewGRFs, rubidium@8378: * so we have to manually add it, and then remove it later. rubidium@8378: */ rubidium@8378: GRFConfig *top = _grfconfig; rubidium@8378: GRFConfig *master = CallocT(1); rubidium@8378: master->filename = strdup(files->openttd.filename); rubidium@8378: FillGRFDetails(master, false); skidd13@8425: ClrBit(master->flags, GCF_INIT_ONLY); rubidium@8378: master->next = top; rubidium@8378: _grfconfig = master; rubidium@8378: rubidium@8378: LoadNewGRF(SPR_NEWGRFS_BASE, i); rubidium@8378: rubidium@8378: /* Free and remove the top element. */ rubidium@8378: ClearGRFConfig(&master); rubidium@8378: _grfconfig = top; truelight@0: } truelight@0: truelight@0: rubidium@6573: void GfxLoadSprites() tron@1093: { Darkvater@5568: DEBUG(sprite, 2, "Loading sprite set %d", _opt.landscape); truelight@0: peter1138@5151: GfxInitSpriteMem(); peter1138@5151: LoadSpriteTables(); peter1138@5151: GfxInitPalettes(); truelight@0: }