spritecache.c
changeset 2340 0a9f3eeccb96
parent 2339 1c64119d5a3b
child 2342 c19fb4f2df30
equal deleted inserted replaced
2339:1c64119d5a3b 2340:0a9f3eeccb96
     2 
     2 
     3 #include "stdafx.h"
     3 #include "stdafx.h"
     4 #include "openttd.h"
     4 #include "openttd.h"
     5 #include "debug.h"
     5 #include "debug.h"
     6 #include "functions.h"
     6 #include "functions.h"
     7 #include "gfx.h"
       
     8 #include "spritecache.h"
     7 #include "spritecache.h"
     9 #include "table/sprites.h"
     8 #include "table/sprites.h"
    10 #include "fileio.h"
     9 #include "fileio.h"
    11 #include "newgrf.h"
    10 #include "newgrf.h"
    12 #include "md5.h"
       
    13 #include "variables.h"
       
    14 #include <ctype.h>
       
    15 
    11 
    16 #define SPRITE_CACHE_SIZE 1024*1024
    12 #define SPRITE_CACHE_SIZE 1024*1024
    17 
    13 
    18 #define WANT_NEW_LRU
    14 #define WANT_NEW_LRU
    19 
    15 
    20 
    16 
    21 /* These are used in newgrf.c: */
    17 /* These are used in newgrf.c: */
    22 
    18 
    23 int _skip_sprites = 0;
    19 extern int _skip_sprites; // XXX
    24 int _replace_sprites_count[16];
    20 extern int _replace_sprites_count[16]; // XXX
    25 int _replace_sprites_offset[16];
    21 extern int _replace_sprites_offset[16]; // XXX
    26 
    22 
    27 static const char *_cur_grffile;
    23 extern const char* _cur_grffile; // XXX
    28 static int _loading_stage;
    24 extern int _loading_stage; // XXX
    29 static int _skip_specials;
    25 extern int _skip_specials; // XXX
    30 uint16 _custom_sprites_base;
    26 static Sprite _cur_sprite; // XXX
    31 static Sprite _cur_sprite;
       
    32 
    27 
    33 
    28 
    34 static void* _sprite_ptr[MAX_SPRITES];
    29 static void* _sprite_ptr[MAX_SPRITES];
    35 static uint32 _sprite_file_pos[MAX_SPRITES];
    30 static uint32 _sprite_file_pos[MAX_SPRITES];
    36 
    31 
    47 } MemBlock;
    42 } MemBlock;
    48 
    43 
    49 static uint _sprite_lru_counter;
    44 static uint _sprite_lru_counter;
    50 static MemBlock *_spritecache_ptr;
    45 static MemBlock *_spritecache_ptr;
    51 static int _compact_cache_counter;
    46 static int _compact_cache_counter;
    52 
       
    53 typedef struct MD5File {
       
    54 	const char * const filename;     // filename
       
    55 	const md5_byte_t hash[16]; // md5 sum of the file
       
    56 } MD5File;
       
    57 
       
    58 typedef struct FileList {
       
    59 	const MD5File basic[5];     // grf files that always have to be loaded
       
    60 	const MD5File landscape[3]; // landscape specific grf files
       
    61 } FileList;
       
    62 
       
    63 #include "table/files.h"
       
    64 #include "table/landscape_sprite.h"
       
    65 
       
    66 static const SpriteID * const _landscape_spriteindexes[] = {
       
    67 	_landscape_spriteindexes_1,
       
    68 	_landscape_spriteindexes_2,
       
    69 	_landscape_spriteindexes_3,
       
    70 };
       
    71 
       
    72 static const SpriteID * const _slopes_spriteindexes[] = {
       
    73 	_slopes_spriteindexes_0,
       
    74 	_slopes_spriteindexes_1,
       
    75 	_slopes_spriteindexes_2,
       
    76 	_slopes_spriteindexes_3,
       
    77 };
       
    78 
    47 
    79 static void CompactSpriteCache(void);
    48 static void CompactSpriteCache(void);
    80 
    49 
    81 static bool ReadSpriteHeaderSkipData(int load_index)
    50 static bool ReadSpriteHeaderSkipData(int load_index)
    82 {
    51 {
   194 		return sprite;
   163 		return sprite;
   195 	}
   164 	}
   196 }
   165 }
   197 
   166 
   198 
   167 
   199 static bool LoadNextSprite(int load_index, byte file_index)
   168 bool LoadNextSprite(int load_index, byte file_index)
   200 {
   169 {
   201 	uint32 file_pos = FioGetPos() | (file_index << 24);
   170 	uint32 file_pos = FioGetPos() | (file_index << 24);
   202 
   171 
   203 	if (!ReadSpriteHeaderSkipData(load_index)) return false;
   172 	if (!ReadSpriteHeaderSkipData(load_index)) return false;
   204 
   173 
   242 #endif
   211 #endif
   243 
   212 
   244 	return true;
   213 	return true;
   245 }
   214 }
   246 
   215 
   247 static void SkipSprites(uint count)
   216 void SkipSprites(uint count)
   248 {
   217 {
   249 	for (; count > 0; --count) {
   218 	for (; count > 0; --count) {
   250 		if (!ReadSpriteHeaderSkipData(MAX_SPRITES - 1)) return;
   219 		if (!ReadSpriteHeaderSkipData(MAX_SPRITES - 1)) return;
   251 	}
       
   252 }
       
   253 
       
   254 static int LoadGrfFile(const char *filename, int load_index, int file_index)
       
   255 {
       
   256 	int load_index_org = load_index;
       
   257 
       
   258 	FioOpenFile(file_index, filename);
       
   259 
       
   260 	/* Thou shalt use LoadNewGrfFile() if thou loadeth a GRF file that
       
   261 	 * might contain some special sprites. */
       
   262 	_skip_specials = 1;
       
   263 	_skip_sprites = 0;
       
   264 
       
   265 	DEBUG(spritecache, 2) ("Reading grf-file ``%s''", filename);
       
   266 
       
   267 	while (LoadNextSprite(load_index, file_index)) {
       
   268 		load_index++;
       
   269 		if (load_index >= MAX_SPRITES) {
       
   270 			error("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
       
   271 		}
       
   272 	}
       
   273 	DEBUG(spritecache, 2) ("Currently %i sprites are loaded", load_index);
       
   274 
       
   275 	return load_index - load_index_org;
       
   276 }
       
   277 
       
   278 static int LoadNewGrfFile(const char *filename, int load_index, int file_index)
       
   279 {
       
   280 	int i;
       
   281 
       
   282 	FioOpenFile(file_index, filename);
       
   283 	_cur_grffile = filename;
       
   284 	_skip_specials = 0;
       
   285 	_skip_sprites = 0;
       
   286 
       
   287 	DEBUG(spritecache, 2) ("Reading newgrf-file ``%s'' [offset: %u]",
       
   288 			filename, load_index);
       
   289 
       
   290 	/* Skip the first sprite; we don't care about how many sprites this
       
   291 	 * does contain; newest TTDPatches and George's longvehicles don't
       
   292 	 * neither, apparently. */
       
   293 	{
       
   294 		int length;
       
   295 		byte type;
       
   296 
       
   297 		length = FioReadWord();
       
   298 		type = FioReadByte();
       
   299 
       
   300 		if (length == 4 && type == 0xFF) {
       
   301 			FioReadDword();
       
   302 		} else {
       
   303 			error("Custom .grf has invalid format.");
       
   304 		}
       
   305 	}
       
   306 
       
   307 	for (i = 0; LoadNextSprite(load_index + i, file_index); i++) {
       
   308 		if (load_index + i >= MAX_SPRITES)
       
   309 			error("Too many sprites (0x%X). Recompile with higher MAX_SPRITES value or remove some custom GRF files.",
       
   310 			      load_index + i);
       
   311 	}
       
   312 
       
   313 	/* Clean up. */
       
   314 	_skip_sprites = 0;
       
   315 	memset(_replace_sprites_count, 0, sizeof(_replace_sprites_count));
       
   316 	memset(_replace_sprites_offset, 0, sizeof(_replace_sprites_offset));
       
   317 
       
   318 	return i;
       
   319 }
       
   320 
       
   321 static void LoadGrfIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
       
   322 {
       
   323 	int start;
       
   324 
       
   325 	FioOpenFile(file_index, filename);
       
   326 	_skip_specials = 1;
       
   327 	_skip_sprites = 0;
       
   328 
       
   329 	DEBUG(spritecache, 2) ("Reading indexed grf-file ``%s''", filename);
       
   330 
       
   331 	for (; (start = *index_tbl++) != 0xffff;) {
       
   332 		int end = *index_tbl++;
       
   333 		if(start == 0xfffe) { // skip sprites (amount in second var)
       
   334 			SkipSprites(end);
       
   335 		} else { // load sprites and use indexes from start to end
       
   336 			do {
       
   337 			#ifdef NDEBUG
       
   338 				LoadNextSprite(start, file_index);
       
   339 			#else
       
   340 				bool b = LoadNextSprite(start, file_index);
       
   341 				assert(b);
       
   342 			#endif
       
   343 			} while (++start <= end);
       
   344 		}
       
   345 	}
   220 	}
   346 }
   221 }
   347 
   222 
   348 
   223 
   349 #define S_FREE_MASK 1
   224 #define S_FREE_MASK 1
   605 	// Load the sprite, if it is not loaded, yet
   480 	// Load the sprite, if it is not loaded, yet
   606 	if (p == NULL) p = ReadSprite(sprite);
   481 	if (p == NULL) p = ReadSprite(sprite);
   607 	return p;
   482 	return p;
   608 }
   483 }
   609 
   484 
   610 byte _sprite_page_to_load = 0xFF;
   485 
   611 
   486 void GfxInitSpriteMem(void)
   612 #define OPENTTD_SPRITES_COUNT 100
       
   613 static const SpriteID _openttd_grf_indexes[] = {
       
   614 	SPR_OPENTTD_BASE + 0, SPR_OPENTTD_BASE + 7, // icons etc
       
   615 	134, 134,  // euro symbol medium size
       
   616 	582, 582,  // euro symbol large size
       
   617 	358, 358,  // euro symbol tiny
       
   618 	SPR_OPENTTD_BASE+11, SPR_OPENTTD_BASE+57, // more icons
       
   619 	648, 648, // nordic char: æ
       
   620 	616, 616, // nordic char: Æ
       
   621 	666, 666, // nordic char: ø
       
   622 	634, 634, // nordic char: Ø
       
   623 	SPR_OPENTTD_BASE+62, SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT, // more icons
       
   624 	0xffff,
       
   625 };
       
   626 
       
   627 /* FUNCTIONS FOR CHECKING MD5 SUMS OF GRF FILES */
       
   628 
       
   629 /* Check that the supplied MD5 hash matches that stored for the supplied filename */
       
   630 static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn)
       
   631 {
       
   632 	uint i;
       
   633 
       
   634 	/* Loop through each byte of the file MD5 and the stored MD5... */
       
   635 	for (i = 0; i < 16; i++) if (file.hash[i] != digest[i]) break;
       
   636 
       
   637 		/* If all bytes of the MD5's match (i.e. the MD5's match)... */
       
   638 	if (i == 16) {
       
   639 		return true;
       
   640 	} else {
       
   641 		if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename);
       
   642 		return false;
       
   643 	};
       
   644 }
       
   645 
       
   646 /* Calculate and check the MD5 hash of the supplied filename.
       
   647  * returns true if the checksum is correct */
       
   648 static bool FileMD5(const MD5File file, bool warn)
       
   649 {
       
   650 	FILE *f;
       
   651 	char buf[MAX_PATH];
       
   652 
       
   653 	// open file
       
   654 	sprintf(buf, "%s%s", _path.data_dir, file.filename);
       
   655 	f = fopen(buf, "rb");
       
   656 
       
   657 #if !defined(WIN32)
       
   658 	if (f == NULL) {
       
   659 		char *s;
       
   660 	// make lower case and check again
       
   661 		for (s = buf + strlen(_path.data_dir) - 1; *s != '\0'; s++)
       
   662 			*s = tolower(*s);
       
   663 		f = fopen(buf, "rb");
       
   664 	}
       
   665 #endif
       
   666 
       
   667 	if (f != NULL) {
       
   668 		md5_state_t filemd5state;
       
   669 		md5_byte_t buffer[1024];
       
   670 		md5_byte_t digest[16];
       
   671 		size_t len;
       
   672 
       
   673 		md5_init(&filemd5state);
       
   674 		while ((len = fread(buffer, 1, sizeof(buffer), f)) != 0)
       
   675 			md5_append(&filemd5state, buffer, len);
       
   676 
       
   677 		if (ferror(f) && warn) fprintf(stderr, "Error Reading from %s \n", buf);
       
   678 		fclose(f);
       
   679 
       
   680 		md5_finish(&filemd5state, digest);
       
   681 	  return CheckMD5Digest(file, digest, warn);
       
   682 	} else { // file not found
       
   683 		return false;
       
   684 	}
       
   685 }
       
   686 
       
   687 /* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF)
       
   688  * by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly.
       
   689  * If neither are found, Windows palette is assumed.
       
   690  *
       
   691  * (Note: Also checks sample.cat for corruption) */
       
   692 void CheckExternalFiles(void)
       
   693 {
       
   694 	uint i;
       
   695 	// count of files from this version
       
   696 	uint dos = 0;
       
   697 	uint win = 0;
       
   698 
       
   699 	for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++;
       
   700 	for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++;
       
   701 
       
   702 	for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++;
       
   703 	for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++;
       
   704 
       
   705 	if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false))
       
   706 		fprintf(stderr, "Your sample.cat file is corrupted or missing!\n");
       
   707 
       
   708 	/*
       
   709 	 * forced DOS palette via command line -> leave it that way
       
   710 	 * all Windows files present -> Windows palette
       
   711 	 * all DOS files present -> DOS palette
       
   712 	 * no Windows files present and any DOS file present -> DOS palette
       
   713 	 * otherwise -> Windows palette
       
   714 	 */
       
   715 	if (_use_dos_palette) {
       
   716 		return;
       
   717 	} else if (win == 5) {
       
   718 		_use_dos_palette = false;
       
   719 	} else if (dos == 5 || (win == 0 && dos > 0)) {
       
   720 		_use_dos_palette = true;
       
   721 	} else {
       
   722 		_use_dos_palette = false;
       
   723 	}
       
   724 }
       
   725 
       
   726 static void LoadSpriteTables(void)
       
   727 {
       
   728 	int load_index = 0;
       
   729 	uint i;
       
   730 	uint j;
       
   731 	const FileList *files; // list of grf files to be loaded. Either Windows files or DOS files
       
   732 
       
   733 	_loading_stage = 1;
       
   734 
       
   735 	/*
       
   736 	 * TODO:
       
   737 	 *   I think we can live entirely without Indexed GRFs, but I have to
       
   738 	 *   invest that further. --octo
       
   739 	 */
       
   740 
       
   741 	files = _use_dos_palette? &files_dos : &files_win;
       
   742 
       
   743 	for (i = 0; files->basic[i].filename != NULL; i++) {
       
   744 		load_index += LoadGrfFile(files->basic[i].filename, load_index, i);
       
   745 	}
       
   746 
       
   747 	LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++);
       
   748 
       
   749 	if (_sprite_page_to_load != 0) {
       
   750 		LoadGrfIndexed(
       
   751 			files->landscape[_sprite_page_to_load - 1].filename,
       
   752 			_landscape_spriteindexes[_sprite_page_to_load - 1],
       
   753 			i++
       
   754 		);
       
   755 	}
       
   756 
       
   757 	LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++);
       
   758 
       
   759 	load_index = SPR_AUTORAIL_BASE;
       
   760 	load_index += LoadGrfFile("autorail.grf", load_index, i++);
       
   761 
       
   762 	load_index = SPR_CANALS_BASE;
       
   763 	load_index += LoadGrfFile("canalsw.grf", load_index, i++);
       
   764 
       
   765 	load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT + 1;
       
   766 
       
   767 
       
   768 	/* Load newgrf sprites
       
   769 	 * in each loading stage, (try to) open each file specified in the config
       
   770 	 * and load information from it. */
       
   771 	_custom_sprites_base = load_index;
       
   772 	for (_loading_stage = 0; _loading_stage < 2; _loading_stage++) {
       
   773 		load_index = _custom_sprites_base;
       
   774 		for (j = 0; j != lengthof(_newgrf_files) && _newgrf_files[j]; j++) {
       
   775 			if (!FiosCheckFileExists(_newgrf_files[j])) {
       
   776 				// TODO: usrerror()
       
   777 				error("NewGRF file missing: %s", _newgrf_files[j]);
       
   778 			}
       
   779 			if (_loading_stage == 0) InitNewGRFFile(_newgrf_files[j], load_index);
       
   780 			load_index += LoadNewGrfFile(_newgrf_files[j], load_index, i++);
       
   781 			DEBUG(spritecache, 2) ("Currently %i sprites are loaded", load_index);
       
   782 		}
       
   783 	}
       
   784 
       
   785 	_compact_cache_counter = 0;
       
   786 }
       
   787 
       
   788 static void GfxInitSpriteMem(void)
       
   789 {
   487 {
   790 	// initialize sprite cache heap
   488 	// initialize sprite cache heap
   791 	if (_spritecache_ptr == NULL) _spritecache_ptr = malloc(SPRITE_CACHE_SIZE);
   489 	if (_spritecache_ptr == NULL) _spritecache_ptr = malloc(SPRITE_CACHE_SIZE);
   792 
   490 
   793 	// A big free block
   491 	// A big free block
   794 	_spritecache_ptr->size = (SPRITE_CACHE_SIZE - sizeof(MemBlock)) | S_FREE_MASK;
   492 	_spritecache_ptr->size = (SPRITE_CACHE_SIZE - sizeof(MemBlock)) | S_FREE_MASK;
   795 	// Sentinel block (identified by size == 0)
   493 	// Sentinel block (identified by size == 0)
   796 	NextBlock(_spritecache_ptr)->size = 0;
   494 	NextBlock(_spritecache_ptr)->size = 0;
   797 
   495 
   798 	memset(_sprite_ptr, 0, sizeof(_sprite_ptr));
   496 	memset(_sprite_ptr, 0, sizeof(_sprite_ptr));
   799 }
   497 
   800 
   498 	_compact_cache_counter = 0;
   801 
   499 }
   802 void GfxLoadSprites(void)
       
   803 {
       
   804 	// Need to reload the sprites only if the landscape changed
       
   805 	if (_sprite_page_to_load != _opt.landscape) {
       
   806 		_sprite_page_to_load = _opt.landscape;
       
   807 
       
   808 		// Sprite cache
       
   809 		DEBUG(spritecache, 1) ("Loading sprite set %d.", _sprite_page_to_load);
       
   810 
       
   811 		GfxInitSpriteMem();
       
   812 		LoadSpriteTables();
       
   813 		GfxInitPalettes();
       
   814 	}
       
   815 }