truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" truelight@0: #include "gfx.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" truelight@0: #include truelight@0: truelight@0: #define SPRITECACHE_ID 0xF00F0006 truelight@0: #define SPRITE_CACHE_SIZE 1024*1024 truelight@0: truelight@0: truelight@0: //#define WANT_SPRITESIZES truelight@0: #define WANT_NEW_LRU truelight@0: //#define WANT_LOCKED truelight@0: truelight@0: dominik@452: /* These are used in newgrf.c: */ darkvater@361: truelight@0: int _skip_sprites = 0; darkvater@361: int _replace_sprites_count[16]; darkvater@361: int _replace_sprites_offset[16]; truelight@0: darkvater@368: static const char *_cur_grffile; darkvater@368: static int _loading_stage; truelight@0: static int _skip_specials; darkvater@392: uint16 _custom_sprites_base; tron@1348: static Sprite _cur_sprite; truelight@0: darkvater@361: truelight@0: static byte *_sprite_ptr[NUM_SPRITES]; truelight@0: static uint16 _sprite_size[NUM_SPRITES]; truelight@0: static uint32 _sprite_file_pos[NUM_SPRITES]; truelight@0: truelight@0: // This one is probably not needed. truelight@0: #if defined(WANT_LOCKED) truelight@0: static bool _sprite_locked[NUM_SPRITES]; truelight@0: #endif truelight@0: truelight@0: #if defined(WANT_NEW_LRU) truelight@0: static int16 _sprite_lru_new[NUM_SPRITES]; truelight@0: #else truelight@0: static uint16 _sprite_lru[NUM_SPRITES]; truelight@0: static uint16 _sprite_lru_cur[NUM_SPRITES]; truelight@0: #endif truelight@0: truelight@0: #ifdef WANT_SPRITESIZES truelight@0: static int8 _sprite_xoffs[NUM_SPRITES]; truelight@0: static int8 _sprite_yoffs[NUM_SPRITES]; truelight@0: static uint16 _sprite_xsize[NUM_SPRITES]; truelight@0: static uint8 _sprite_ysize[NUM_SPRITES]; truelight@0: #endif truelight@0: tron@1353: typedef struct MemBlock { tron@1353: uint32 size; tron@1353: byte data[VARARRAY_SIZE]; tron@1353: } MemBlock; tron@1353: truelight@0: static uint _sprite_lru_counter; tron@1353: static MemBlock *_spritecache_ptr; truelight@0: static uint32 _spritecache_size; truelight@0: static int _compact_cache_counter; 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: dominik@961: #include "table/files.h" truelight@0: #include "table/landscape_sprite.h" truelight@0: truelight@0: static const uint16 * const _landscape_spriteindexes[] = { truelight@0: _landscape_spriteindexes_1, truelight@0: _landscape_spriteindexes_2, truelight@0: _landscape_spriteindexes_3, truelight@0: }; truelight@0: dominik@37: static const uint16 * 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: tron@1093: static void CompactSpriteCache(void); truelight@0: truelight@0: static void ReadSpriteHeaderSkipData(int num, int load_index) truelight@0: { truelight@0: byte type; truelight@0: int deaf = 0; truelight@0: truelight@0: if (_skip_sprites) { truelight@0: if (_skip_sprites > 0) truelight@0: _skip_sprites--; truelight@0: deaf = 1; truelight@0: } truelight@184: truelight@0: type = FioReadByte(); darkvater@364: _cur_sprite.info = type; truelight@0: if (type == 0xFF) { truelight@0: /* We need to really skip only special sprites in the deaf truelight@0: * mode. It won't hurt to proceed regular sprites as usual truelight@0: * because if no special sprite referencing to them is truelight@0: * processed, they themselves are never referenced and loaded truelight@0: * on their own. */ truelight@0: if (_skip_specials || deaf) { truelight@0: FioSkipBytes(num); truelight@0: } else { darkvater@368: DecodeSpecialSprite(_cur_grffile, num, load_index, _loading_stage); truelight@0: } truelight@0: return; truelight@0: } truelight@184: truelight@0: #ifdef WANT_SPRITESIZES truelight@0: _cur_sprite.height = FioReadByte(); truelight@0: _cur_sprite.width = FioReadWord(); truelight@0: _cur_sprite.x_offs = FioReadWord(); truelight@0: _cur_sprite.y_offs = FioReadWord(); truelight@0: #else truelight@0: FioSkipBytes(7); truelight@0: #endif truelight@0: num -= 8; truelight@0: if (num == 0) truelight@0: return; truelight@0: truelight@0: if (type & 2) { truelight@0: FioSkipBytes(num); tron@1355: } else { tron@1355: while (num > 0) { tron@1355: int8 i = FioReadByte(); tron@1355: if (i >= 0) { tron@1355: num -= i; tron@1355: FioSkipBytes(i); tron@1355: } else { tron@1355: i = -(i >> 3); tron@1355: num -= i; tron@1355: FioReadByte(); tron@1355: } truelight@0: } truelight@0: } truelight@0: } truelight@0: tron@1361: static void ReadSprite(SpriteID id, void *buffer) truelight@0: { tron@1354: uint num = _sprite_size[id]; truelight@0: byte type; tron@1361: byte* dest; truelight@184: tron@1378: if (_sprite_file_pos[id] == 0) { tron@1378: error( tron@1378: "Tried to load non-existing sprite #%d.\n" tron@1378: "Probable cause: Wrong/missing NewGRFs", tron@1378: id tron@1378: ); tron@1378: } tron@1378: tron@1354: FioSeekToFile(_sprite_file_pos[id]); tron@1354: truelight@0: type = FioReadByte(); truelight@0: /* We've decoded special sprites when reading headers. */ truelight@0: if (type != 0xFF) { truelight@0: /* read sprite hdr */ tron@1361: Sprite* sprite = buffer; tron@1351: sprite->info = type; tron@1351: sprite->height = FioReadByte(); tron@1354: if (id == 142) sprite->height = 10; // Compensate for a TTD bug tron@1351: sprite->width = FioReadWord(); tron@1351: sprite->x_offs = FioReadWord(); tron@1351: sprite->y_offs = FioReadWord(); tron@1351: dest = sprite->data; truelight@0: num -= 8; tron@1361: } else { tron@1361: dest = buffer; truelight@0: } truelight@0: truelight@0: if (type & 2) { tron@1355: for (; num > 0; --num) truelight@0: *dest++ = FioReadByte(); tron@1355: } else { tron@1355: while (num > 0) { tron@1355: int8 i = FioReadByte(); truelight@0: tron@1355: if (i >= 0) { tron@1355: num -= i; tron@1355: for (; i > 0; --i) tron@1355: *dest++ = FioReadByte(); tron@1355: } else { tron@1355: const byte* rel = dest - (((i & 7) << 8) | FioReadByte()); truelight@184: tron@1355: i = -(i >> 3); tron@1355: num -= i; tron@1355: tron@1355: for (; i > 0; --i) tron@1355: *dest++ = *rel++; tron@1355: } truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: truelight@0: static bool LoadNextSprite(int load_index, byte file_index) truelight@0: { truelight@0: uint16 size; darkvater@361: uint32 file_pos; truelight@0: tron@1355: size = FioReadWord(); tron@1355: if (size == 0) truelight@0: return false; truelight@0: darkvater@361: file_pos = FioGetPos() | (file_index << 24); truelight@0: truelight@0: ReadSpriteHeaderSkipData(size, load_index); truelight@0: tron@1355: if (_replace_sprites_count[0] > 0 && _cur_sprite.info != 0xFF) { darkvater@361: int count = _replace_sprites_count[0]; darkvater@361: int offset = _replace_sprites_offset[0]; darkvater@361: darkvater@361: _replace_sprites_offset[0]++; darkvater@361: _replace_sprites_count[0]--; truelight@862: darkvater@361: if ((offset + count) <= NUM_SPRITES) { darkvater@361: load_index = offset; darkvater@361: } else { darkvater@361: DEBUG(spritecache, 1) ("Sprites to be replaced are out of range: %x+%x", darkvater@361: count, offset); darkvater@361: _replace_sprites_offset[0] = 0; darkvater@361: _replace_sprites_count[0] = 0; darkvater@361: } darkvater@361: darkvater@361: if (_replace_sprites_count[0] == 0) { darkvater@361: int i; darkvater@361: darkvater@361: for (i = 0; i < 15; i++) { darkvater@361: _replace_sprites_count[i] = _replace_sprites_count[i + 1]; darkvater@361: _replace_sprites_offset[i] = _replace_sprites_offset[i + 1]; darkvater@361: } darkvater@361: _replace_sprites_count[i] = 0; darkvater@361: _replace_sprites_offset[i] = 0; darkvater@361: } darkvater@361: } darkvater@361: darkvater@361: _sprite_size[load_index] = size; darkvater@361: _sprite_file_pos[load_index] = file_pos; darkvater@361: truelight@0: #ifdef WANT_SPRITESIZES truelight@0: _sprite_xsize[load_index] = _cur_sprite.width; truelight@0: _sprite_ysize[load_index] = _cur_sprite.height; truelight@0: truelight@0: _sprite_xoffs[load_index] = _cur_sprite.x_offs; truelight@0: _sprite_yoffs[load_index] = _cur_sprite.y_offs; truelight@0: #endif truelight@0: truelight@0: _sprite_ptr[load_index] = NULL; truelight@0: #if defined(WANT_LOCKED) truelight@0: _sprite_locked[load_index] = false; truelight@0: #endif truelight@0: truelight@0: #if defined(WANT_NEW_LRU) truelight@0: _sprite_lru_new[load_index] = 0; truelight@184: #else truelight@0: _sprite_lru[load_index] = 0xFFFF; truelight@0: _sprite_lru_cur[load_index] = 0; truelight@0: #endif truelight@0: truelight@0: return true; truelight@0: } truelight@0: tron@1355: static void SkipSprites(uint count) dominik@37: { tron@1355: for (; count > 0; --count) dominik@37: { tron@1355: uint16 size = FioReadWord(); tron@1355: tron@1355: if (size == 0) dominik@37: return; dominik@37: tron@1355: ReadSpriteHeaderSkipData(size, NUM_SPRITES - 1); dominik@37: } dominik@37: } dominik@37: truelight@0: static int LoadGrfFile(const char *filename, int load_index, int file_index) truelight@0: { truelight@0: int load_index_org = load_index; truelight@0: truelight@0: FioOpenFile(file_index, filename); darkvater@365: darkvater@365: /* Thou shalt use LoadNewGrfFile() if thou loadeth a GRF file that darkvater@365: * might contain some special sprites. */ darkvater@365: _skip_specials = 1; darkvater@365: _skip_sprites = 0; 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++; truelight@0: if (load_index >= NUM_SPRITES) { truelight@0: error("Too many sprites. Recompile with higher NUM_SPRITES value or remove some custom GRF files."); truelight@0: } truelight@0: } truelight@0: darkvater@365: return load_index - load_index_org; darkvater@365: } darkvater@365: darkvater@365: static int LoadNewGrfFile(const char *filename, int load_index, int file_index) darkvater@365: { darkvater@365: int i; dominik@614: darkvater@365: FioOpenFile(file_index, filename); darkvater@368: _cur_grffile = filename; darkvater@365: _skip_specials = 0; darkvater@365: _skip_sprites = 0; darkvater@365: darkvater@365: DEBUG(spritecache, 2) ("Reading newgrf-file ``%s'' [offset: %u]", darkvater@366: filename, load_index); darkvater@365: miham@442: /* Skip the first sprite; we don't care about how many sprites this miham@442: * does contain; newest TTDPatches and George's longvehicles don't miham@442: * neither, apparently. */ darkvater@365: { darkvater@365: int length; darkvater@365: byte type; darkvater@365: darkvater@365: length = FioReadWord(); darkvater@365: type = FioReadByte(); darkvater@365: tron@1355: if (length == 4 && type == 0xFF) { miham@442: FioReadDword(); darkvater@365: } else { darkvater@365: error("Custom .grf has invalid format."); darkvater@365: } darkvater@365: } darkvater@365: miham@442: for (i = 0; LoadNextSprite(load_index + i, file_index); i++) { miham@442: if (load_index + i >= NUM_SPRITES) miham@442: error("Too many sprites (%x). Recompile with higher NUM_SPRITES value or remove some custom GRF files.", miham@442: load_index + i); darkvater@365: } darkvater@365: darkvater@361: /* Clean up. */ darkvater@361: _skip_sprites = 0; darkvater@361: memset(_replace_sprites_count, 0, 16 * sizeof(*_replace_sprites_count)); darkvater@361: memset(_replace_sprites_offset, 0, 16 * sizeof(*_replace_sprites_offset)); truelight@0: miham@442: return i; truelight@0: } truelight@0: truelight@0: static void LoadGrfIndexed(const char *filename, const uint16 *index_tbl, int file_index) truelight@0: { dominik@37: int start; truelight@0: truelight@0: FioOpenFile(file_index, filename); darkvater@365: _skip_specials = 1; darkvater@365: _skip_sprites = 0; darkvater@365: darkvater@366: DEBUG(spritecache, 2) ("Reading indexed grf-file ``%s''", filename); truelight@0: tron@1355: for (; (start = *index_tbl++) != 0xffff;) { dominik@37: int end = *index_tbl++; tron@1355: if(start == 0xfffe) { // 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: tron@1355: typedef size_t CDECL fread_t(void*, size_t, size_t, FILE*); truelight@0: truelight@0: static bool HandleCachedSpriteHeaders(const char *filename, bool read) truelight@0: { truelight@0: FILE *f; truelight@0: fread_t *proc; truelight@0: uint32 hdr; truelight@0: truelight@0: if (!_cache_sprites) truelight@0: return false; truelight@0: truelight@0: if (read) { truelight@0: f = fopen(filename, "rb"); truelight@0: proc = fread; truelight@0: truelight@0: if (f == NULL) truelight@0: return false; truelight@0: truelight@0: proc(&hdr, sizeof(hdr), 1, f); truelight@0: if (hdr != SPRITECACHE_ID) { truelight@0: fclose(f); truelight@0: return false; truelight@0: } truelight@0: } else { truelight@0: f = fopen(filename, "wb"); truelight@0: proc = (fread_t*) fwrite; truelight@0: truelight@0: if (f == NULL) truelight@0: return false; truelight@0: truelight@0: hdr = SPRITECACHE_ID; truelight@0: proc(&hdr, sizeof(hdr), 1, f); truelight@0: } truelight@0: truelight@0: proc(_sprite_size, 1, sizeof(_sprite_size), f); truelight@0: proc(_sprite_file_pos, 1, sizeof(_sprite_file_pos), f); truelight@0: truelight@0: #if 0 truelight@0: proc(_sprite_xsize, 1, sizeof(_sprite_xsize), f); truelight@0: proc(_sprite_ysize, 1, sizeof(_sprite_ysize), f); truelight@0: proc(_sprite_xoffs, 1, sizeof(_sprite_xoffs), f); truelight@0: proc(_sprite_yoffs, 1, sizeof(_sprite_yoffs), f); truelight@0: #endif truelight@0: truelight@0: #if !defined(WANT_NEW_LRU) truelight@0: if (read) truelight@0: memset(_sprite_lru, 0xFF, sizeof(_sprite_lru)); truelight@0: #endif truelight@0: truelight@0: fclose(f); truelight@0: return true; truelight@0: } truelight@0: truelight@0: #define S_FREE_MASK 1 tron@1353: tron@1353: static inline MemBlock* NextBlock(MemBlock* block) tron@1353: { tron@1353: return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK)); tron@1353: } truelight@0: tron@1093: static uint32 GetSpriteCacheUsage(void) truelight@0: { tron@1353: size_t tot_size = 0; tron@1353: MemBlock* s; tron@1353: tron@1353: for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) tron@1353: if (!(s->size & S_FREE_MASK)) tot_size += s->size; truelight@0: truelight@0: return tot_size; truelight@0: } truelight@0: truelight@0: tron@1093: void IncreaseSpriteLRU(void) truelight@0: { truelight@0: int i; truelight@0: truelight@0: // Increase all LRU values truelight@0: #if defined(WANT_NEW_LRU) truelight@0: if (_sprite_lru_counter > 16384) { truelight@0: DEBUG(spritecache, 2) ("fixing lru %d, inuse=%d", _sprite_lru_counter, GetSpriteCacheUsage()); truelight@0: tron@1355: for (i = 0; i != NUM_SPRITES; i++) truelight@0: if (_sprite_ptr[i] != NULL) { truelight@0: if (_sprite_lru_new[i] >= 0) { truelight@0: _sprite_lru_new[i] = -1; truelight@0: } else if (_sprite_lru_new[i] != -32768) { truelight@0: _sprite_lru_new[i]--; truelight@0: } truelight@0: } truelight@0: _sprite_lru_counter = 0; truelight@0: } truelight@0: #else tron@1355: for (i = 0; i != NUM_SPRITES; i++) truelight@0: if (_sprite_ptr[i] != NULL && _sprite_lru[i] != 65535) truelight@0: _sprite_lru[i]++; truelight@0: // Reset the lru counter. truelight@0: _sprite_lru_counter = 0; truelight@0: #endif truelight@0: truelight@0: // Compact sprite cache every now and then. truelight@0: if (++_compact_cache_counter >= 740) { truelight@0: CompactSpriteCache(); truelight@0: _compact_cache_counter = 0; truelight@0: } truelight@0: } truelight@0: truelight@0: // Called when holes in the sprite cache should be removed. truelight@0: // That is accomplished by moving the cached data. tron@1093: static void CompactSpriteCache(void) truelight@0: { tron@1353: MemBlock *s; truelight@184: tron@1353: DEBUG(spritecache, 2) ( tron@1353: "compacting sprite cache, inuse=%d", GetSpriteCacheUsage() tron@1353: ); truelight@184: tron@1353: for (s = _spritecache_ptr; s->size != 0;) { tron@1353: if (s->size & S_FREE_MASK) { tron@1353: MemBlock* next = NextBlock(s); tron@1353: MemBlock temp; tron@1353: byte** i; tron@1353: truelight@0: // Since free blocks are automatically coalesced, this should hold true. tron@1353: assert(!(next->size & S_FREE_MASK)); truelight@184: truelight@0: // If the next block is the sentinel block, we can safely return tron@1353: if (next->size == 0) truelight@0: break; truelight@0: tron@1353: // Locate the sprite belonging to the next pointer. tron@1353: for (i = _sprite_ptr; *i != next->data; ++i) { tron@1353: assert(i != endof(_sprite_ptr)); tron@1353: } truelight@0: tron@1353: #ifdef WANT_LOCKED tron@1353: if (_sprite_locked[i]) { tron@1353: s = next; tron@1353: continue; tron@1353: } tron@1353: #endif truelight@184: tron@1353: *i = s->data; // Adjust sprite array entry tron@1353: // Swap this and the next block tron@1353: temp = *s; tron@1353: memmove(s, next, next->size); tron@1353: s = NextBlock(s); tron@1353: *s = temp; tron@1353: tron@1353: // Coalesce free blocks tron@1353: while (NextBlock(s)->size & S_FREE_MASK) { tron@1353: s->size += NextBlock(s)->size & ~S_FREE_MASK; truelight@0: } tron@1353: } else { tron@1353: s = NextBlock(s); truelight@0: } truelight@0: } truelight@0: } truelight@0: tron@1093: static void DeleteEntryFromSpriteCache(void) truelight@0: { truelight@0: int i; truelight@0: int best = -1; tron@1353: MemBlock* s; truelight@0: int cur_lru; truelight@0: truelight@0: DEBUG(spritecache, 2) ("DeleteEntryFromSpriteCache, inuse=%d", GetSpriteCacheUsage()); truelight@0: truelight@0: #if defined(WANT_NEW_LRU) truelight@0: cur_lru = 0xffff; tron@1355: for (i = 0; i != NUM_SPRITES; i++) { truelight@0: if (_sprite_ptr[i] != 0 && truelight@0: _sprite_lru_new[i] < cur_lru truelight@0: #if defined(WANT_LOCKED) tron@1355: && !_sprite_locked[i] tron@1355: #endif truelight@0: ) { truelight@0: cur_lru = _sprite_lru_new[i]; truelight@0: best = i; truelight@0: } truelight@0: } truelight@0: #else truelight@0: { truelight@0: uint16 cur_lru = 0, cur_lru_cur = 0xffff; tron@1355: for (i = 0; i != NUM_SPRITES; i++) { truelight@0: if (_sprite_ptr[i] == 0 || truelight@0: #if defined(WANT_LOCKED) truelight@0: _sprite_locked[i] || truelight@0: #endif truelight@0: _sprite_lru[i] < cur_lru) truelight@0: continue; truelight@184: truelight@0: // Found a sprite with a higher LRU value, then remember it. truelight@0: if (_sprite_lru[i] != cur_lru) { truelight@0: cur_lru = _sprite_lru[i]; truelight@0: best = i; truelight@0: truelight@0: // Else if both sprites were very recently referenced, compare by the cur value instead. truelight@0: } else if (cur_lru == 0 && _sprite_lru_cur[i] <= cur_lru_cur) { truelight@0: cur_lru_cur = _sprite_lru_cur[i]; truelight@0: cur_lru = _sprite_lru[i]; truelight@0: best = i; truelight@0: } truelight@0: } truelight@0: } truelight@0: #endif truelight@0: truelight@0: // Display an error message and die, in case we found no sprite at all. truelight@0: // This shouldn't really happen, unless all sprites are locked. truelight@0: if (best == -1) truelight@0: error("Out of sprite memory"); truelight@0: truelight@0: // Mark the block as free (the block must be in use) tron@1353: s = (MemBlock*)_sprite_ptr[best] - 1; tron@1353: assert(!(s->size & S_FREE_MASK)); tron@1353: s->size |= S_FREE_MASK; truelight@0: _sprite_ptr[best] = NULL; truelight@0: truelight@0: // And coalesce adjacent free blocks tron@1353: for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) { tron@1353: if (s->size & S_FREE_MASK) { tron@1353: while (NextBlock(s)->size & S_FREE_MASK) { tron@1353: s->size += NextBlock(s)->size & ~S_FREE_MASK; truelight@0: } truelight@0: } truelight@0: } truelight@0: } truelight@0: tron@1355: static byte *LoadSpriteToMem(SpriteID sprite) truelight@0: { tron@1353: size_t mem_req; truelight@0: truelight@0: DEBUG(spritecache, 9) ("load sprite %d", sprite); truelight@0: truelight@0: // Number of needed bytes tron@1353: mem_req = sizeof(MemBlock) + _sprite_size[sprite]; truelight@0: tron@1353: /* Align this to an uint32 boundary. This also makes sure that the 2 least tron@1353: * bits are not used, so we could use those for other things. */ truelight@0: mem_req = (mem_req + sizeof(uint32) - 1) & ~(sizeof(uint32) - 1); truelight@0: tron@1353: for (;;) { tron@1353: MemBlock* s; truelight@0: tron@1353: for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) { tron@1353: if (s->size & S_FREE_MASK) { tron@1353: size_t cur_size = s->size & ~S_FREE_MASK; truelight@0: tron@1353: /* Is the block exactly the size we need or tron@1353: * big enough for an additional free block? */ tron@1353: if (cur_size == mem_req || tron@1353: cur_size >= mem_req + sizeof(MemBlock)) { tron@1353: // Set size and in use tron@1353: s->size = mem_req; truelight@184: tron@1353: // Do we need to inject a free block too? tron@1353: if (cur_size != mem_req) { tron@1353: NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK; tron@1353: } truelight@0: tron@1353: _sprite_ptr[sprite] = s->data; tron@1353: tron@1354: ReadSprite(sprite, s->data); tron@1353: tron@1353: // Return sprite ptr tron@1353: return s->data; tron@1353: } tron@1353: } truelight@0: } truelight@0: tron@1353: // Reached sentinel, but no block found yet. Delete some old entry. tron@1353: DeleteEntryFromSpriteCache(); truelight@0: } truelight@0: } truelight@0: truelight@0: #if defined(NEW_ROTATION) truelight@0: #define X15(x) else if (s >= x && s < (x+15)) { s = _rotate_tile_sprite[s - x] + x; } truelight@0: #define X19(x) else if (s >= x && s < (x+19)) { s = _rotate_tile_sprite[s - x] + x; } truelight@0: #define MAP(from,to,map) else if (s >= from && s <= to) { s = map[s - from] + from; } truelight@0: truelight@0: tron@410: static uint RotateSprite(uint s) truelight@0: { truelight@0: static const byte _rotate_tile_sprite[19] = { 0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,17,18,16,15 }; truelight@0: static const byte _coast_map[9] = {0, 4, 3, 1, 2, 6, 8, 5, 7}; truelight@0: static const byte _fence_map[6] = {1, 0, 5, 4, 3, 2}; truelight@0: truelight@0: if (0); truelight@0: X19(752) truelight@0: X15(990-1) truelight@0: X19(3924) truelight@0: X19(3943) truelight@0: X19(3962) truelight@0: X19(3981) truelight@0: X19(4000) truelight@0: X19(4023) truelight@0: X19(4042) truelight@0: MAP(4061,4069,_coast_map) truelight@0: X19(4126) truelight@0: X19(4145) truelight@0: X19(4164) truelight@0: X19(4183) truelight@0: X19(4202) truelight@0: X19(4221) truelight@0: X19(4240) truelight@0: X19(4259) truelight@0: X19(4259) truelight@0: X19(4278) truelight@0: MAP(4090, 4095, _fence_map) truelight@0: MAP(4096, 4101, _fence_map) truelight@0: MAP(4102, 4107, _fence_map) truelight@0: MAP(4108, 4113, _fence_map) truelight@0: MAP(4114, 4119, _fence_map) truelight@0: MAP(4120, 4125, _fence_map) truelight@0: return s; truelight@0: } truelight@0: #endif truelight@0: tron@1361: const void *GetRawSprite(SpriteID sprite) truelight@0: { truelight@0: byte *p; truelight@0: truelight@0: assert(sprite < NUM_SPRITES); truelight@0: truelight@0: #if defined(NEW_ROTATION) truelight@0: sprite = RotateSprite(sprite); truelight@0: #endif truelight@0: truelight@0: // Update LRU truelight@0: #if defined(WANT_NEW_LRU) truelight@0: _sprite_lru_new[sprite] = ++_sprite_lru_counter; truelight@0: #else truelight@0: _sprite_lru_cur[sprite] = ++_sprite_lru_counter; truelight@0: _sprite_lru[sprite] = 0; truelight@0: #endif truelight@0: truelight@0: // Check if the sprite is loaded already? truelight@0: p = _sprite_ptr[sprite]; truelight@0: if (p == NULL) truelight@0: p = LoadSpriteToMem(sprite); // No, need to load it. truelight@184: return p; truelight@0: } truelight@0: truelight@0: byte _sprite_page_to_load = 0xFF; truelight@0: truelight@0: static const char * const _cached_filenames[4] = { truelight@0: "cached_sprites.xxx", truelight@0: "cached_sprites.xx1", truelight@0: "cached_sprites.xx2", truelight@0: "cached_sprites.xx3", truelight@0: }; truelight@0: darkvater@884: #define OPENTTD_SPRITES_COUNT 98 truelight@0: static const uint16 _openttd_grf_indexes[] = { truelight@0: SPR_OPENTTD_BASE+0, SPR_OPENTTD_BASE+7, // icons etc dominik@579: 134, 134, // euro symbol medium size dominik@579: 582, 582, // euro symbol large size dominik@579: 358, 358, // euro symbol tiny dominik@579: SPR_OPENTTD_BASE+11, SPR_OPENTTD_BASE+57, // more icons truelight@0: 648, 648, // nordic char: æ truelight@0: 616, 616, // nordic char: Æ truelight@0: 666, 666, // nordic char: Ø truelight@0: 634, 634, // nordic char: Ø dominik@143: SPR_OPENTTD_BASE+62, SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT, // more icons truelight@0: 0xffff, truelight@0: }; truelight@0: dominik@961: /* FUNCTIONS FOR CHECKING MD5 SUMS OF GRF FILES */ dominik@614: 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: { dominik@961: int i, matching_bytes=0; dominik@961: dominik@961: /* Loop through each byte of the file MD5 and the stored MD5... */ dominik@961: for (i = 0; i < 16; i++) dominik@961: { dominik@961: if (file.hash[i] == digest[i]) dominik@961: matching_bytes++; dominik@961: }; dominik@961: dominik@961: /* If all bytes of the MD5's match (i.e. the MD5's match)... */ dominik@961: if (matching_bytes == 16) { dominik@961: return true; dominik@961: } else { dominik@961: if (warn) printf("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: md5_state_t filemd5state; dominik@961: int len=0; dominik@961: md5_byte_t buffer[1024], digest[16]; dominik@961: 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) { dominik@961: md5_init(&filemd5state); tron@1355: while ((len = fread(buffer, 1, 1024, f)) != 0) dominik@961: md5_append(&filemd5state, buffer, len); dominik@961: dominik@961: if (ferror(f)) tron@1355: if (warn) printf("Error Reading from %s \n", buf); dominik@961: fclose(f); tron@1019: dominik@961: md5_finish(&filemd5state, digest); dominik@961: 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@1355: for (i = 0; i < 2; i++) tron@1355: if (FileMD5(files_dos.basic[i], true)) dominik@961: dos++; tron@1355: for (i = 0; i < 3; i++) tron@1355: if (FileMD5(files_dos.landscape[i], true)) dominik@961: dos++; dominik@961: tron@1355: for (i = 0; i < 2; i++) tron@1355: if (FileMD5(files_win.basic[i], true)) dominik@961: win++; tron@1355: for (i = 0; i < 3; i++) tron@1355: if (FileMD5(files_win.landscape[i], true)) dominik@961: win++; dominik@961: tron@1355: if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false)) dominik@961: printf("Your sample.cat file is corrupted or missing!"); 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@1093: static void LoadSpriteTables(void) truelight@0: { tron@1355: uint i; tron@1355: uint j; tron@1355: const FileList *files; // list of grf files to be loaded. Either Windows files or DOS files truelight@0: darkvater@368: _loading_stage = 1; darkvater@368: truelight@184: /* truelight@184: * Note for developers: truelight@184: * Keep in mind that when you add a LoadGrfIndexed in the 'if'-section below truelight@184: * that you should also add the corresponding FioOpenFile to the 'else'-section truelight@184: * below. darkvater@364: * darkvater@364: * TODO: darkvater@364: * I think we can live entirely without Indexed GRFs, but I have to darkvater@364: * invest that further. --octo truelight@184: */ truelight@184: tron@1355: files = _use_dos_palette? &files_dos : &files_win; dominik@614: truelight@184: // Try to load the sprites from cache truelight@0: if (!HandleCachedSpriteHeaders(_cached_filenames[_opt.landscape], true)) { truelight@184: // We do not have the sprites in cache yet, or cache is disabled truelight@184: // So just load all files from disk.. truelight@184: truelight@0: int load_index = 0; darkvater@368: tron@1355: for (i = 0; files->basic[i].filename != NULL; i++) { dominik@961: load_index += LoadGrfFile(files->basic[i].filename, load_index, (byte)i); truelight@0: } truelight@0: truelight@0: LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++); truelight@0: truelight@184: if (_sprite_page_to_load != 0) tron@1355: LoadGrfIndexed( tron@1355: files->landscape[_sprite_page_to_load - 1].filename, tron@1355: _landscape_spriteindexes[_sprite_page_to_load - 1], tron@1355: i++ tron@1355: ); truelight@184: dominik@37: LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++); truelight@0: dominik@1070: load_index = SPR_AUTORAIL_BASE; dominik@1070: load_index += LoadGrfFile("autorail.grf", load_index, i++); dominik@1070: truelight@0: load_index = SPR_CANALS_BASE; truelight@0: load_index += LoadGrfFile("canalsw.grf", load_index, i++); dominik@142: truelight@184: load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT + 1; dominik@142: darkvater@368: tron@1355: /* Load newgrf sprites tron@1355: * in each loading stage, (try to) open each file specified in the config tron@1355: * and load information from it. */ darkvater@392: _custom_sprites_base = load_index; dominik@1198: for (_loading_stage = 0; _loading_stage < 2; _loading_stage++) { darkvater@1250: load_index = _custom_sprites_base; dominik@1198: for (j = 0; j != lengthof(_newgrf_files) && _newgrf_files[j]; j++) { tron@1355: if (!FiosCheckFileExists(_newgrf_files[j])) pasky@1437: // TODO: usrerror() pasky@1437: error("NewGRF file missing: %s", _newgrf_files[j]); dominik@1198: if (_loading_stage == 0) dominik@1198: InitNewGRFFile(_newgrf_files[j], load_index); dominik@1198: load_index += LoadNewGrfFile(_newgrf_files[j], load_index, i++); dominik@1198: } darkvater@366: } truelight@0: truelight@184: // If needed, save the cache to file truelight@0: HandleCachedSpriteHeaders(_cached_filenames[_opt.landscape], false); truelight@0: } else { truelight@184: // We have sprites cached. We just loaded the cached files truelight@184: // now we only have to open a file-pointer to all the original grf-files truelight@184: // This is very important. Not all sprites are in the cache. So sometimes truelight@184: // the game needs to load the sprite from disk. When the file is not truelight@184: // open it can not read. So all files that are in the 'if'-section truelight@184: // above should also be in this 'else'-section. truelight@184: // truelight@184: // NOTE: the order of the files must be identical as in the section above!! truelight@184: tron@1355: for (i = 0; files->basic[i].filename != NULL; i++) dominik@961: FioOpenFile(i,files->basic[i].filename); truelight@184: truelight@184: FioOpenFile(i++, "openttd.grf"); truelight@184: truelight@0: if (_sprite_page_to_load != 0) tron@1355: FioOpenFile(i++, files->landscape[_sprite_page_to_load - 1].filename); truelight@184: truelight@184: FioOpenFile(i++, "trkfoundw.grf"); truelight@184: FioOpenFile(i++, "canalsw.grf"); truelight@184: truelight@184: // FIXME: if a user changes his newgrf's, the cached-sprites gets truelight@184: // invalid. We should have some kind of check for this. truelight@184: // The best solution for this is to delete the cached-sprites.. but how truelight@184: // do we detect it? tron@1355: for (j = 0; j != lengthof(_newgrf_files) && _newgrf_files[j] != NULL; j++) truelight@184: FioOpenFile(i++, _newgrf_files[j]); truelight@0: } truelight@0: truelight@0: _compact_cache_counter = 0; truelight@0: } truelight@0: tron@1353: static void GfxInitSpriteMem(void *ptr, uint32 size) truelight@0: { truelight@0: // initialize sprite cache heap truelight@0: _spritecache_ptr = ptr; truelight@0: _spritecache_size = size; truelight@0: truelight@0: // A big free block tron@1353: _spritecache_ptr->size = (size - sizeof(MemBlock)) | S_FREE_MASK; tron@1353: // Sentinel block (identified by size == 0) tron@1353: NextBlock(_spritecache_ptr)->size = 0; truelight@0: truelight@0: memset(_sprite_ptr, 0, sizeof(_sprite_ptr)); truelight@0: } truelight@0: truelight@0: tron@1093: void GfxLoadSprites(void) tron@1093: { truelight@0: static byte *_sprite_mem; truelight@184: 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: truelight@0: // Reuse existing memory? truelight@0: if (_sprite_mem == NULL) _sprite_mem = malloc(SPRITE_CACHE_SIZE); truelight@0: GfxInitSpriteMem(_sprite_mem, SPRITE_CACHE_SIZE); truelight@0: LoadSpriteTables(); truelight@0: GfxInitPalettes(); truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1349: const SpriteDimension *GetSpriteDimension(SpriteID sprite) truelight@0: { truelight@0: static SpriteDimension sd_static; tron@1355: SpriteDimension *sd = &sd_static; truelight@0: tron@1355: #ifdef WANT_SPRITESIZES tron@1355: sd->xoffs = _sprite_xoffs[sprite]; tron@1355: sd->yoffs = _sprite_yoffs[sprite]; tron@1355: sd->xsize = _sprite_xsize[sprite]; tron@1355: sd->ysize = _sprite_ysize[sprite]; tron@1355: #else tron@1355: const Sprite* p = GetSprite(sprite); truelight@0: truelight@0: /* decode sprite header */ tron@1351: sd->xoffs = p->x_offs; tron@1351: sd->yoffs = p->y_offs; tron@1351: sd->xsize = p->width; tron@1348: sd->ysize = p->height; truelight@0: #endif tron@1355: truelight@0: return sd; truelight@0: } truelight@184: