src/gfxinit.cpp
branchnoai
changeset 9722 ebf0ece7d8f6
parent 9703 d2a6acdbd665
child 9723 eee46cb39750
equal deleted inserted replaced
9721:9a27928bcd5e 9722:ebf0ece7d8f6
     9 #include "gfx.h"
     9 #include "gfx.h"
    10 #include "gfxinit.h"
    10 #include "gfxinit.h"
    11 #include "spritecache.h"
    11 #include "spritecache.h"
    12 #include "table/sprites.h"
    12 #include "table/sprites.h"
    13 #include "fileio.h"
    13 #include "fileio.h"
       
    14 #include "fios.h"
    14 #include "string.h"
    15 #include "string.h"
    15 #include "newgrf.h"
    16 #include "newgrf.h"
    16 #include "md5.h"
    17 #include "md5.h"
    17 #include "variables.h"
    18 #include "variables.h"
    18 #include "fontcache.h"
    19 #include "fontcache.h"
    22 	const char * filename;     ///< filename
    23 	const char * filename;     ///< filename
    23 	md5_byte_t hash[16];       ///< md5 sum of the file
    24 	md5_byte_t hash[16];       ///< md5 sum of the file
    24 };
    25 };
    25 
    26 
    26 struct FileList {
    27 struct FileList {
    27 	MD5File basic[4];          ///< grf files that always have to be loaded
    28 	MD5File basic[2];     ///< GRF files that always have to be loaded
    28 	MD5File landscape[3];      ///< landscape specific grf files
    29 	MD5File landscape[3]; ///< Landscape specific grf files
    29 };
    30 	MD5File sound;        ///< Sound samples
    30 
    31 	MD5File chars;        ///< GRF File with character replacements
    31 enum {
    32 	MD5File openttd;      ///< GRF File with OTTD specific graphics
    32 	SKIP = 0xFFFE,
       
    33 	END  = 0xFFFF
       
    34 };
    33 };
    35 
    34 
    36 #include "table/files.h"
    35 #include "table/files.h"
    37 #include "table/landscape_sprite.h"
    36 #include "table/landscape_sprite.h"
    38 
    37 
    40 	_landscape_spriteindexes_1,
    39 	_landscape_spriteindexes_1,
    41 	_landscape_spriteindexes_2,
    40 	_landscape_spriteindexes_2,
    42 	_landscape_spriteindexes_3,
    41 	_landscape_spriteindexes_3,
    43 };
    42 };
    44 
    43 
    45 static const SpriteID * const _slopes_spriteindexes[] = {
    44 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
    46 	_slopes_spriteindexes_0,
       
    47 	_slopes_spriteindexes_1,
       
    48 	_slopes_spriteindexes_2,
       
    49 	_slopes_spriteindexes_3,
       
    50 };
       
    51 
       
    52 
       
    53 static uint LoadGrfFile(const char* filename, uint load_index, int file_index)
       
    54 {
    45 {
    55 	uint load_index_org = load_index;
    46 	uint load_index_org = load_index;
    56 	uint sprite_id = 0;
    47 	uint sprite_id = 0;
    57 
    48 
    58 	FioOpenFile(file_index, filename);
    49 	FioOpenFile(file_index, filename);
    70 
    61 
    71 	return load_index - load_index_org;
    62 	return load_index - load_index_org;
    72 }
    63 }
    73 
    64 
    74 
    65 
    75 static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index)
    66 void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl)
    76 {
    67 {
    77 	uint start;
    68 	uint start;
    78 	uint sprite_id = 0;
       
    79 
       
    80 	FioOpenFile(file_index, filename);
       
    81 
       
    82 	DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
       
    83 
       
    84 	while ((start = *index_tbl++) != END) {
    69 	while ((start = *index_tbl++) != END) {
    85 		uint end = *index_tbl++;
    70 		uint end = *index_tbl++;
    86 
    71 
    87 		if (start == SKIP) { // skip sprites (amount in second var)
    72 		if (start == SKIP) { // skip sprites (amount in second var)
    88 			SkipSprites(end);
    73 			SkipSprites(end);
    89 			sprite_id += end;
    74 			(*sprite_id) += end;
    90 		} else { // load sprites and use indexes from start to end
    75 		} else { // load sprites and use indexes from start to end
    91 			do {
    76 			do {
    92 			#ifdef NDEBUG
    77 			#ifdef NDEBUG
    93 				LoadNextSprite(start, file_index, sprite_id);
    78 				LoadNextSprite(start, file_index, *sprite_id);
    94 			#else
    79 			#else
    95 				bool b = LoadNextSprite(start, file_index, sprite_id);
    80 				bool b = LoadNextSprite(start, file_index, *sprite_id);
    96 				assert(b);
    81 				assert(b);
    97 			#endif
    82 			#endif
    98 				sprite_id++;
    83 				(*sprite_id)++;
    99 			} while (++start <= end);
    84 			} while (++start <= end);
   100 		}
    85 		}
   101 	}
    86 	}
   102 }
    87 }
   103 
    88 
   104 
    89 static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index)
   105 /* Check that the supplied MD5 hash matches that stored for the supplied filename */
    90 {
   106 static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn)
    91 	uint sprite_id = 0;
   107 {
    92 
   108 	if (memcmp(file.hash, digest, sizeof(file.hash)) == 0) return true;
    93 	FioOpenFile(file_index, filename);
   109 	if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
    94 
   110 	return false;
    95 	DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
   111 }
    96 
   112 
    97 	LoadSpritesIndexed(file_index, &sprite_id, index_tbl);
   113 /* Calculate and check the MD5 hash of the supplied filename.
    98 }
   114  * returns true if the checksum is correct */
    99 
   115 static bool FileMD5(const MD5File file, bool warn)
   100 
       
   101 /**
       
   102  * Calculate and check the MD5 hash of the supplied filename.
       
   103  * @param file filename and expected MD5 hash for the given filename.
       
   104  * @return true if the checksum is correct.
       
   105  */
       
   106 static bool FileMD5(const MD5File file)
   116 {
   107 {
   117 	size_t size;
   108 	size_t size;
   118 	FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size);
   109 	FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size);
   119 
   110 
   120 	if (f != NULL) {
   111 	if (f != NULL) {
   127 		while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
   118 		while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
   128 			size -= len;
   119 			size -= len;
   129 			md5_append(&filemd5state, buffer, len);
   120 			md5_append(&filemd5state, buffer, len);
   130 		}
   121 		}
   131 
   122 
   132 		if (ferror(f) && warn) ShowInfoF("Error Reading from %s \n", file.filename);
       
   133 		FioFCloseFile(f);
   123 		FioFCloseFile(f);
   134 
   124 
   135 		md5_finish(&filemd5state, digest);
   125 		md5_finish(&filemd5state, digest);
   136 		return CheckMD5Digest(file, digest, warn);
   126 		return memcmp(file.hash, digest, sizeof(file.hash)) == 0;
   137 	} else { // file not found
   127 	} else { // file not found
   138 		return false;
   128 		return false;
   139 	}
   129 	}
   140 }
   130 }
   141 
   131 
   142 /* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF)
   132 /**
   143  * by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly.
   133  * Determine the palette that has to be used.
   144  * If neither are found, Windows palette is assumed.
   134  *  - forced DOS palette via command line -> leave it that way
   145  *
   135  *  - all Windows files present -> Windows palette
   146  * (Note: Also checks sample.cat for corruption) */
   136  *  - all DOS files present -> DOS palette
   147 void CheckExternalFiles()
   137  *  - no Windows files present and any DOS file present -> DOS palette
   148 {
   138  *  - otherwise -> Windows palette
   149 	uint i;
   139  */
   150 	/* count of files from this version */
   140 static void DeterminePalette()
       
   141 {
       
   142 	if (_use_dos_palette) return;
       
   143 
       
   144 	/* Count of files from the different versions. */
   151 	uint dos = 0;
   145 	uint dos = 0;
   152 	uint win = 0;
   146 	uint win = 0;
   153 
   147 
   154 	for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++;
   148 	for (uint i = 0; i < lengthof(files_dos.basic); i++) if (FioCheckFileExists(files_dos.basic[i].filename)) dos++;
   155 	for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++;
   149 	for (uint i = 0; i < lengthof(files_dos.landscape); i++) if (FioCheckFileExists(files_dos.landscape[i].filename)) dos++;
   156 
   150 
   157 	for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++;
   151 	for (uint i = 0; i < lengthof(files_win.basic); i++) if (FioCheckFileExists(files_win.basic[i].filename)) win++;
   158 	for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++;
   152 	for (uint i = 0; i < lengthof(files_win.landscape); i++) if (FioCheckFileExists(files_win.landscape[i].filename)) win++;
   159 
   153 
   160 	if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false))
   154 	if (win == 5) {
   161 		ShowInfo("Your 'sample.cat' file is corrupted or missing!");
       
   162 
       
   163 	for (i = 0; i < lengthof(files_openttd); i++) {
       
   164 		if (!FileMD5(files_openttd[i], false)) {
       
   165 			ShowInfoF("Your '%s' file is corrupted or missing!", files_openttd[i].filename);
       
   166 		}
       
   167 	}
       
   168 
       
   169 	/*
       
   170 	 * forced DOS palette via command line -> leave it that way
       
   171 	 * all Windows files present -> Windows palette
       
   172 	 * all DOS files present -> DOS palette
       
   173 	 * no Windows files present and any DOS file present -> DOS palette
       
   174 	 * otherwise -> Windows palette
       
   175 	 */
       
   176 	if (_use_dos_palette) {
       
   177 		return;
       
   178 	} else if (win == 5) {
       
   179 		_use_dos_palette = false;
   155 		_use_dos_palette = false;
   180 	} else if (dos == 5 || (win == 0 && dos > 0)) {
   156 	} else if (dos == 5 || (win == 0 && dos > 0)) {
   181 		_use_dos_palette = true;
   157 		_use_dos_palette = true;
   182 	} else {
   158 	} else {
   183 		_use_dos_palette = false;
   159 		_use_dos_palette = false;
   184 	}
   160 	}
       
   161 }
       
   162 
       
   163 /**
       
   164  * Checks whether the MD5 checksums of the files are correct.
       
   165  *
       
   166  * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption.
       
   167  */
       
   168 void CheckExternalFiles()
       
   169 {
       
   170 	DeterminePalette();
       
   171 
       
   172 	static const size_t ERROR_MESSAGE_LENGTH = 128;
       
   173 	const FileList *files = _use_dos_palette ? &files_dos : &files_win;
       
   174 	char error_msg[ERROR_MESSAGE_LENGTH * (lengthof(files->basic) + lengthof(files->landscape) + 3)];
       
   175 	error_msg[0] = '\0';
       
   176 	char *add_pos = error_msg;
       
   177 
       
   178 	for (uint i = 0; i < lengthof(files->basic); i++) {
       
   179 		if (!FileMD5(files->basic[i])) {
       
   180 			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);
       
   181 		}
       
   182 	}
       
   183 
       
   184 	for (uint i = 0; i < lengthof(files->landscape); i++) {
       
   185 		if (!FileMD5(files->landscape[i])) {
       
   186 			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);
       
   187 		}
       
   188 	}
       
   189 
       
   190 	if (!FileMD5(files_win.sound) && !FileMD5(files_dos.sound)) {
       
   191 		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");
       
   192 	}
       
   193 
       
   194 	if (!FileMD5(files->chars)) {
       
   195 		add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! The file was part of your installation.\n", files->chars.filename);
       
   196 	}
       
   197 
       
   198 	if (!FileMD5(files->openttd)) {
       
   199 		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);
       
   200 	}
       
   201 
       
   202 	if (add_pos != error_msg) ShowInfoF(error_msg);
   185 }
   203 }
   186 
   204 
   187 
   205 
   188 static const SpriteID trg1idx[] = {
   206 static const SpriteID trg1idx[] = {
   189 	   0,    1, ///< Mouse cursor, ZZZ
   207 	   0,    1, ///< Mouse cursor, ZZZ
   263 /* Graphics */
   281 /* Graphics */
   264 	 674, 4792,
   282 	 674, 4792,
   265 	END
   283 	END
   266 };
   284 };
   267 
   285 
   268 /* NOTE: When adding a normal sprite, increase OPENTTD_SPRITES_COUNT with the
   286 /** Replace some letter sprites with some other letters */
   269  * amount of sprites and add them to the end of the list, with the index of
   287 static const SpriteID _chars_grf_indexes[] = {
   270  * the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no
   288 	134, 134, ///<  euro symbol medium size
   271  * correspondence of any kind with the ID's in the grf file, but results in
   289 	582, 582, ///<  euro symbol large size
   272  * a maximum use of sprite slots. */
   290 	358, 358, ///<  euro symbol tiny
   273 static const SpriteID _openttd_grf_indexes[] = {
       
   274 	SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc
       
   275 	134, 134,  ///< euro symbol medium size
       
   276 	582, 582,  ///<  euro symbol large size
       
   277 	358, 358,  ///<  euro symbol tiny
       
   278 	SPR_CURSOR_CANAL, SPR_IMG_FASTFORWARD, // more icons
       
   279 	648, 648, ///<  nordic char: æ
   291 	648, 648, ///<  nordic char: æ
   280 	616, 616, ///<  nordic char: Æ
   292 	616, 616, ///<  nordic char: Æ
   281 	666, 666, ///<  nordic char: ø
   293 	666, 666, ///<  nordic char: ø
   282 	634, 634, ///<  nordic char: Ø
   294 	634, 634, ///<  nordic char: Ø
   283 	SPR_PIN_UP, SPR_CURSOR_CLONE_TRAIN, // more icons
       
   284 	382, 383, ///<  Œ œ tiny
   295 	382, 383, ///<  Œ œ tiny
   285 	158, 159, ///<  Œ œ medium
   296 	158, 159, ///<  Œ œ medium
   286 	606, 607, ///<  Œ œ large
   297 	606, 607, ///<  Œ œ large
   287 	360, 360, ///<  Š tiny
   298 	360, 360, ///<  Š tiny
   288 	362, 362, ///<  š tiny
   299 	362, 362, ///<  š tiny
   304 	156, 156, ///<  º medium
   315 	156, 156, ///<  º medium
   305 	604, 604, ///<  º large
   316 	604, 604, ///<  º large
   306 	317, 320, ///<  { | } ~ tiny
   317 	317, 320, ///<  { | } ~ tiny
   307 	 93,  96, ///<  { | } ~ medium
   318 	 93,  96, ///<  { | } ~ medium
   308 	541, 544, ///<  { | } ~ large
   319 	541, 544, ///<  { | } ~ large
   309 	SPR_HOUSE_ICON, SPR_HOUSE_ICON,
       
   310 	585, 585, ///<  § large
   320 	585, 585, ///<  § large
   311 	587, 587, ///<  © large
   321 	587, 587, ///<  © large
   312 	592, 592, ///<  ® large
   322 	592, 592, ///<  ® large
   313 	594, 597, ///<  ° ± ² ³ large
   323 	594, 597, ///<  ° ± ² ³ large
   314 	633, 633, ///<  × large
   324 	633, 633, ///<  × large
   315 	665, 665, ///<  ÷ large
   325 	665, 665, ///<  ÷ large
   316 	SPR_SELL_TRAIN, SPR_SHARED_ORDERS_ICON,
       
   317 	377, 377, ///<  · small
   326 	377, 377, ///<  · small
   318 	153, 153, ///<  · medium
   327 	153, 153, ///<  · medium
   319 	601, 601, ///<  · large
   328 	601, 601, ///<  · large
   320 	SPR_WARNING_SIGN, SPR_WARNING_SIGN,
       
   321 	END
   329 	END
   322 };
   330 };
   323 
   331 
   324 
   332 
   325 static void LoadSpriteTables()
   333 static void LoadSpriteTables()
   326 {
   334 {
   327 	const FileList* files = _use_dos_palette ? &files_dos : &files_win;
   335 	const FileList *files = _use_dos_palette ? &files_dos : &files_win;
   328 	uint load_index;
   336 	uint i = FIRST_GRF_SLOT;
   329 	uint i;
   337 
   330 
   338 	LoadGrfIndexed(files->basic[0].filename, trg1idx, i++);
   331 	LoadGrfIndexed(files->basic[0].filename, trg1idx, 0);
       
   332 	DupSprite(  2, 130); // non-breaking space medium
   339 	DupSprite(  2, 130); // non-breaking space medium
   333 	DupSprite(226, 354); // non-breaking space tiny
   340 	DupSprite(226, 354); // non-breaking space tiny
   334 	DupSprite(450, 578); // non-breaking space large
   341 	DupSprite(450, 578); // non-breaking space large
   335 	load_index = 4793;
   342 
   336 
   343 	/*
   337 	for (i = 1; files->basic[i].filename != NULL; i++) {
   344 	 * The second basic file always starts at the given location and does
   338 		load_index += LoadGrfFile(files->basic[i].filename, load_index, i);
   345 	 * contain a different amount of sprites depending on the "type"; DOS
   339 	}
   346 	 * has a few sprites less. However, we do not care about those missing
   340 
   347 	 * sprites as they are not shown anyway (logos in intro game).
   341 	/* Load additional sprites for climates other than temperate */
   348 	 */
       
   349 	LoadGrfFile(files->basic[1].filename, 4793, i++);
       
   350 
       
   351 	/*
       
   352 	 * Load additional sprites for climates other than temperate.
       
   353 	 * This overwrites some of the temperate sprites, such as foundations
       
   354 	 * and the ground sprites.
       
   355 	 */
   342 	if (_opt.landscape != LT_TEMPERATE) {
   356 	if (_opt.landscape != LT_TEMPERATE) {
   343 		LoadGrfIndexed(
   357 		LoadGrfIndexed(
   344 			files->landscape[_opt.landscape - 1].filename,
   358 			files->landscape[_opt.landscape - 1].filename,
   345 			_landscape_spriteindexes[_opt.landscape - 1],
   359 			_landscape_spriteindexes[_opt.landscape - 1],
   346 			i++
   360 			i++
   347 		);
   361 		);
   348 	}
   362 	}
   349 
   363 
   350 	assert(load_index == SPR_SIGNALS_BASE);
   364 	LoadGrfIndexed(files->chars.filename, _chars_grf_indexes, i++);
   351 	load_index += LoadGrfFile("nsignalsw.grf", load_index, i++);
       
   352 
       
   353 	assert(load_index == SPR_CANALS_BASE);
       
   354 	load_index += LoadGrfFile("canalsw.grf", load_index, i++);
       
   355 
       
   356 	assert(load_index == SPR_SLOPES_BASE);
       
   357 	LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++);
       
   358 
       
   359 	load_index = SPR_AUTORAIL_BASE;
       
   360 	load_index += LoadGrfFile("autorail.grf", load_index, i++);
       
   361 
       
   362 	assert(load_index == SPR_ELRAIL_BASE);
       
   363 	load_index += LoadGrfFile("elrailsw.grf", load_index, i++);
       
   364 
       
   365 	assert(load_index == SPR_2CCMAP_BASE);
       
   366 	load_index += LoadGrfFile("2ccmap.grf", load_index, i++);
       
   367 
       
   368 	assert(load_index == SPR_OPENTTD_BASE);
       
   369 	LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++);
       
   370 	load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT;
       
   371 
       
   372 	assert(load_index == SPR_AIRPORTX_BASE);
       
   373 	load_index += LoadGrfFile("airports.grf", load_index, i++);
       
   374 
       
   375 	assert(load_index == SPR_ROADSTOP_BASE);
       
   376 	load_index += LoadGrfFile("roadstops.grf", load_index, i++);
       
   377 
       
   378 	assert(load_index == SPR_GROUP_BASE);
       
   379 	load_index += LoadGrfFile("group.grf", load_index, i++);
       
   380 
       
   381 	assert(load_index == SPR_TRAMWAY_BASE);
       
   382 	load_index += LoadGrfFile("tramtrkw.grf", load_index, i++);
       
   383 
       
   384 	assert(load_index == SPR_ONEWAY_BASE);
       
   385 	load_index += LoadGrfFile("oneway.grf", load_index, i++);
       
   386 
       
   387 	load_index++; // SPR_EMPTY_BOUNDING_BOX
       
   388 
       
   389 	assert(load_index == SPR_FLAGS_BASE);
       
   390 	load_index += LoadGrfFile("flags.grf", load_index, i++);
       
   391 
   365 
   392 	/* Initialize the unicode to sprite mapping table */
   366 	/* Initialize the unicode to sprite mapping table */
   393 	InitializeUnicodeGlyphMap();
   367 	InitializeUnicodeGlyphMap();
   394 
   368 
   395 	LoadNewGRF(load_index, i);
   369 	/*
       
   370 	 * Load the base NewGRF with OTTD required graphics as first NewGRF.
       
   371 	 * However, we do not want it to show up in the list of used NewGRFs,
       
   372 	 * so we have to manually add it, and then remove it later.
       
   373 	 */
       
   374 	GRFConfig *top = _grfconfig;
       
   375 	GRFConfig *master = CallocT<GRFConfig>(1);
       
   376 	master->filename = strdup(files->openttd.filename);
       
   377 	FillGRFDetails(master, false);
       
   378 	ClrBit(master->flags, GCF_INIT_ONLY);
       
   379 	master->next = top;
       
   380 	_grfconfig = master;
       
   381 
       
   382 	LoadNewGRF(SPR_NEWGRFS_BASE, i);
       
   383 
       
   384 	/* Free and remove the top element. */
       
   385 	ClearGRFConfig(&master);
       
   386 	_grfconfig = top;
   396 }
   387 }
   397 
   388 
   398 
   389 
   399 void GfxLoadSprites()
   390 void GfxLoadSprites()
   400 {
   391 {