tron@2186: /* $Id$ */ tron@2186: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" tron@2163: #include "functions.h" truelight@0: #include "gfx.h" tron@2340: #include "gfxinit.h" tron@1349: #include "spritecache.h" tron@1363: #include "table/sprites.h" truelight@0: #include "fileio.h" pasky@463: #include "newgrf.h" dominik@961: #include "md5.h" tron@2159: #include "variables.h" truelight@0: #include truelight@0: dominik@961: typedef struct MD5File { dominik@961: const char * const filename; // filename darkvater@962: const md5_byte_t hash[16]; // md5 sum of the file dominik@961: } MD5File; dominik@961: dominik@614: typedef struct FileList { Darkvater@1725: const MD5File basic[5]; // grf files that always have to be loaded dominik@961: const MD5File landscape[3]; // landscape specific grf files dominik@614: } FileList; truelight@0: tron@2588: enum { tron@2588: SKIP = 0xFFFE, tron@2588: END = 0xFFFF tron@2588: }; tron@2588: 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: celestar@2187: static const SpriteID * const _slopes_spriteindexes[] = { dominik@37: _slopes_spriteindexes_0, dominik@37: _slopes_spriteindexes_1, dominik@37: _slopes_spriteindexes_2, dominik@37: _slopes_spriteindexes_3, dominik@37: }; dominik@37: dominik@37: tron@2342: static uint LoadGrfFile(const char* filename, uint load_index, int file_index) truelight@0: { tron@2342: uint load_index_org = load_index; truelight@0: truelight@0: FioOpenFile(file_index, filename); darkvater@365: darkvater@366: DEBUG(spritecache, 2) ("Reading grf-file ``%s''", filename); truelight@0: truelight@0: while (LoadNextSprite(load_index, file_index)) { truelight@0: load_index++; 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: } celestar@2187: DEBUG(spritecache, 2) ("Currently %i sprites are loaded", load_index); truelight@0: darkvater@365: return load_index - load_index_org; darkvater@365: } darkvater@365: tron@2342: tron@2342: static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index) darkvater@365: { tron@2342: uint start; dominik@614: darkvater@365: FioOpenFile(file_index, filename); darkvater@365: darkvater@366: DEBUG(spritecache, 2) ("Reading indexed grf-file ``%s''", filename); truelight@0: tron@2588: while ((start = *index_tbl++) != END) { tron@2342: uint end = *index_tbl++; tron@2342: tron@2588: if (start == SKIP) { // skip sprites (amount in second var) dominik@37: SkipSprites(end); dominik@37: } else { // load sprites and use indexes from start to end dominik@37: do { Darkvater@1844: #ifdef NDEBUG Darkvater@1844: LoadNextSprite(start, file_index); Darkvater@1844: #else dominik@37: bool b = LoadNextSprite(start, file_index); dominik@37: assert(b); Darkvater@1844: #endif dominik@37: } while (++start <= end); dominik@37: } truelight@0: } truelight@184: } truelight@0: truelight@0: dominik@961: /* Check that the supplied MD5 hash matches that stored for the supplied filename */ dominik@961: static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn) dominik@961: { tron@2028: uint i; dominik@961: dominik@961: /* Loop through each byte of the file MD5 and the stored MD5... */ tron@2028: for (i = 0; i < 16; i++) if (file.hash[i] != digest[i]) break; dominik@961: dominik@961: /* If all bytes of the MD5's match (i.e. the MD5's match)... */ tron@2028: if (i == 16) { dominik@961: return true; dominik@961: } else { tron@2028: if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename); dominik@961: return false; dominik@961: }; dominik@961: } dominik@961: tron@1019: /* Calculate and check the MD5 hash of the supplied filename. dominik@961: * returns true if the checksum is correct */ dominik@961: static bool FileMD5(const MD5File file, bool warn) dominik@614: { dominik@614: FILE *f; dominik@961: char buf[MAX_PATH]; dominik@614: dominik@961: // open file dominik@961: sprintf(buf, "%s%s", _path.data_dir, file.filename); dominik@961: f = fopen(buf, "rb"); dominik@961: dominik@961: #if !defined(WIN32) dominik@961: if (f == NULL) { tron@1329: char *s; dominik@961: // make lower case and check again tron@1355: for (s = buf + strlen(_path.data_dir) - 1; *s != '\0'; s++) dominik@961: *s = tolower(*s); dominik@961: f = fopen(buf, "rb"); dominik@614: } dominik@961: #endif dominik@614: truelight@862: if (f != NULL) { tron@2028: md5_state_t filemd5state; tron@2028: md5_byte_t buffer[1024]; tron@2028: md5_byte_t digest[16]; tron@2028: size_t len; tron@2028: dominik@961: md5_init(&filemd5state); tron@2028: while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0) dominik@961: md5_append(&filemd5state, buffer, len); dominik@961: tron@2028: if (ferror(f) && warn) fprintf(stderr, "Error Reading from %s \n", buf); dominik@961: fclose(f); tron@1019: dominik@961: md5_finish(&filemd5state, digest); tron@2639: return CheckMD5Digest(file, digest, warn); dominik@961: } else { // file not found dominik@961: return false; tron@1019: } dominik@961: } dominik@961: dominik@961: /* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF) dominik@961: * by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly. tron@1019: * If neither are found, Windows palette is assumed. dominik@961: * dominik@961: * (Note: Also checks sample.cat for corruption) */ tron@1093: void CheckExternalFiles(void) dominik@961: { tron@1355: uint i; tron@1355: // count of files from this version tron@1355: uint dos = 0; tron@1355: uint win = 0; dominik@961: tron@2028: for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++; tron@2028: for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++; dominik@961: tron@2028: for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++; tron@2028: for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++; dominik@961: tron@1355: if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false)) tron@2028: fprintf(stderr, "Your sample.cat file is corrupted or missing!\n"); dominik@961: tron@1380: /* tron@1389: * forced DOS palette via command line -> leave it that way tron@1380: * all Windows files present -> Windows palette tron@1380: * all DOS files present -> DOS palette tron@1380: * no Windows files present and any DOS file present -> DOS palette tron@1380: * otherwise -> Windows palette tron@1380: */ tron@1389: if (_use_dos_palette) { tron@1389: return; tron@1389: } else 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: tron@2342: tron@2353: static const SpriteID trg1idx[] = { tron@2588: 0, 1, // Mouse cursor, ZZZ tron@2353: /* Medium font */ tron@2588: 2, 92, // ' ' till 'z' tron@2588: SKIP, 36, tron@2588: 160, 160, // Move ¾ to the correct position tron@2588: 98, 98, // Up arrow tron@2588: 131, 133, tron@2588: SKIP, 1, // skip currency sign tron@2588: 135, 135, tron@2588: SKIP, 1, tron@2588: 137, 137, tron@2588: SKIP, 1, tron@2588: 139, 139, tron@2588: 140, 140, // TODO Down arrow tron@2588: 141, 141, tron@2588: 142, 142, // TODO Check mark tron@2588: 143, 143, // TODO Cross tron@2588: 144, 144, tron@2588: 145, 145, // TODO Right arrow tron@2588: 146, 149, tron@2588: 118, 122, // Transport markers tron@2588: SKIP, 2, tron@2588: 157, 157, tron@2588: 114, 115, // Small up/down arrows tron@2588: SKIP, 1, tron@2588: 161, 225, tron@2353: /* Small font */ tron@2588: 226, 316, // ' ' till 'z' tron@2588: SKIP, 36, tron@2588: 384, 384, // Move ¾ to the correct position tron@2588: 322, 322, // Up arrow tron@2588: 355, 357, tron@2588: SKIP, 1, // skip currency sign tron@2588: 359, 359, tron@2588: SKIP, 1, tron@2588: 361, 361, tron@2588: SKIP, 1, tron@2588: 363, 363, tron@2588: 364, 364, // TODO Down arrow tron@2588: 365, 366, tron@2588: SKIP, 1, tron@2588: 368, 368, tron@2588: 369, 369, // TODO Right arrow tron@2588: 370, 373, tron@2588: SKIP, 7, tron@2588: 381, 381, tron@2588: SKIP, 3, tron@2588: 385, 449, tron@2353: /* Big font */ tron@2588: 450, 540, // ' ' till 'z' tron@2588: SKIP, 36, tron@2588: 608, 608, // Move ¾ to the correct position tron@2588: SKIP, 1, tron@2588: 579, 581, tron@2588: SKIP, 1, tron@2588: 583, 583, tron@2588: SKIP, 5, tron@2588: 589, 589, tron@2588: SKIP, 15, tron@2588: 605, 605, tron@2588: SKIP, 3, tron@2588: 609, 625, tron@2588: SKIP, 1, tron@2588: 627, 632, tron@2588: SKIP, 1, tron@2588: 634, 639, tron@2588: SKIP, 1, tron@2588: 641, 657, tron@2588: SKIP, 1, tron@2588: 659, 664, tron@2588: SKIP, 2, tron@2588: 667, 671, tron@2588: SKIP, 1, tron@2588: 673, 673, tron@2353: /* Graphics */ tron@2588: 674, 4792, tron@2588: END tron@2353: }; tron@2353: Darkvater@2565: /* NOTE: When adding a normal sprite, increase OPENTTD_SPRITES_COUNT with the Darkvater@2565: * amount of sprites and add them to the end of the list, with the index of Darkvater@2565: * the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no Darkvater@2565: * correspondence of any kind with the ID's in the grf file, but results in Darkvater@2565: * a maximum use of sprite slots. */ tron@2577: #define OPENTTD_SPRITES_COUNT 95 tron@2353: static const SpriteID _openttd_grf_indexes[] = { tron@2571: SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc tron@2353: 134, 134, // euro symbol medium size tron@2353: 582, 582, // euro symbol large size tron@2353: 358, 358, // euro symbol tiny tron@2571: SPR_CURSOR_CANAL, SPR_IMG_FASTFORWARD, // more icons tron@2353: 648, 648, // nordic char: æ tron@2353: 616, 616, // nordic char: Æ tron@2353: 666, 666, // nordic char: ø tron@2353: 634, 634, // nordic char: Ø tron@2571: SPR_PIN_UP, SPR_CURSOR_CLONE, // more icons tron@2353: 382, 383, // ¼ ½ tiny tron@2353: 158, 159, // ¼ ½ medium tron@2353: 606, 607, // ¼ ½ large tron@2353: 360, 360, // ¦ tiny tron@2353: 362, 362, // ¨ tiny tron@2353: 136, 136, // ¦ medium tron@2353: 138, 138, // ¨ medium tron@2353: 584, 584, // ¦ large tron@2353: 586, 586, // ¨ large tron@2353: 626, 626, // Ð large tron@2353: 658, 658, // ð large tron@2353: 374, 374, // ´ tiny tron@2353: 378, 378, // ¸ tiny tron@2353: 150, 150, // ´ medium tron@2353: 154, 154, // ¸ medium tron@2353: 598, 598, // ´ large tron@2353: 602, 602, // ¸ large tron@2353: 640, 640, // Þ large tron@2353: 672, 672, // þ large tron@2353: 380, 380, // º tiny tron@2353: 156, 156, // º medium tron@2353: 604, 604, // º large tron@2411: 317, 320, // { | } ~ tiny tron@2411: 93, 96, // { | } ~ medium tron@2411: 541, 544, // { | } ~ large tron@2571: SPR_HOUSE_ICON, SPR_HOUSE_ICON, Darkvater@9939: 585, 585, // § large Darkvater@9939: 587, 587, // © large Darkvater@9939: 592, 592, // ® large Darkvater@9939: 594, 597, // ° ± ² ³ large Darkvater@9939: 633, 633, // × large Darkvater@9939: 665, 665, // ÷ large tron@2588: END tron@2353: }; tron@2353: tron@2342: static byte _sprite_page_to_load = 0xFF; tron@2342: tron@1093: static void LoadSpriteTables(void) truelight@0: { tron@2639: const FileList* files = _use_dos_palette ? &files_dos : &files_win; tron@2639: uint load_index; tron@1355: uint i; dominik@614: tron@2353: LoadGrfIndexed(files->basic[0].filename, trg1idx, 0); tron@2407: DupSprite( 2, 130); // non-breaking space medium tron@2407: DupSprite(226, 354); // non-breaking space tiny tron@2407: DupSprite(450, 578); // non-breaking space large tron@2353: load_index = 4793; tron@2353: tron@2353: for (i = 1; files->basic[i].filename != NULL; i++) { tron@2309: load_index += LoadGrfFile(files->basic[i].filename, load_index, i); tron@2309: } truelight@0: tron@2309: if (_sprite_page_to_load != 0) { tron@2309: LoadGrfIndexed( tron@2309: files->landscape[_sprite_page_to_load - 1].filename, tron@2309: _landscape_spriteindexes[_sprite_page_to_load - 1], tron@2309: i++ tron@2309: ); tron@2309: } truelight@0: tron@2512: assert(load_index == SPR_CANALS_BASE); tron@2512: load_index += LoadGrfFile("canalsw.grf", load_index, i++); tron@2512: tron@2512: assert(load_index == SPR_SLOPES_BASE); tron@2309: LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++); dominik@1070: tron@2309: load_index = SPR_AUTORAIL_BASE; tron@2309: load_index += LoadGrfFile("autorail.grf", load_index, i++); dominik@142: tron@2512: assert(load_index == SPR_OPENTTD_BASE); tron@2512: LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++); tron@2577: load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT; dominik@142: tron@2342: LoadNewGRF(load_index, i); truelight@0: } truelight@0: truelight@0: tron@1093: void GfxLoadSprites(void) tron@1093: { truelight@0: // Need to reload the sprites only if the landscape changed truelight@0: if (_sprite_page_to_load != _opt.landscape) { truelight@0: _sprite_page_to_load = _opt.landscape; truelight@0: truelight@0: // Sprite cache truelight@0: DEBUG(spritecache, 1) ("Loading sprite set %d.", _sprite_page_to_load); truelight@0: tron@2339: GfxInitSpriteMem(); truelight@0: LoadSpriteTables(); truelight@0: GfxInitPalettes(); truelight@0: } truelight@0: }