truelight@0: #include "stdafx.h" truelight@0: #include "ttd.h" truelight@0: #include "gfx.h" truelight@0: #include "fileio.h" pasky@463: #include "newgrf.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; truelight@0: static SpriteHdr _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: truelight@0: static uint _sprite_lru_counter; truelight@0: static byte *_spritecache_ptr; truelight@0: static uint32 _spritecache_size; truelight@0: static int _compact_cache_counter; truelight@0: truelight@0: truelight@0: static const char * const _filename_list[] = { truelight@0: "TRG1R.GRF", truelight@0: "TRGIR.GRF", truelight@0: "signalsw.grf", //0x1320 - 0x1405 inclusive truelight@184: // "openttd.grf", //0x1406 - truelight@0: NULL truelight@0: }; truelight@0: truelight@0: static const char * const _landscape_filenames[] = { truelight@0: "TRGCR.GRF", truelight@0: "TRGHR.GRF", truelight@0: "TRGTR.GRF" truelight@0: }; truelight@0: truelight@0: 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: truelight@0: static void CompactSpriteCache(); truelight@0: truelight@0: static void ReadSpriteHeaderSkipData(int num, int load_index) truelight@0: { truelight@0: byte type; truelight@0: int8 i; 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); truelight@0: return; truelight@0: } truelight@0: truelight@0: while (num) { truelight@0: i = FioReadByte(); truelight@0: if (i>=0) { truelight@0: num -= i; truelight@0: FioSkipBytes(i); truelight@0: } else { truelight@0: i = -(i >> 3); truelight@0: num -= i; truelight@0: FioReadByte(); truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: static void ReadSprite(int num, byte *dest) truelight@0: { truelight@0: byte type; truelight@0: byte *rel; truelight@0: int8 i; truelight@0: int j, dist; truelight@184: truelight@0: type = FioReadByte(); truelight@0: /* We've decoded special sprites when reading headers. */ truelight@0: if (type != 0xFF) { truelight@0: /* read sprite hdr */ truelight@0: *dest++ = type; truelight@0: for(j=0; j!=7; j++) truelight@0: *dest++ = FioReadByte(); truelight@0: num -= 8; truelight@0: } truelight@0: truelight@0: if (type & 2) { truelight@0: while (num--) truelight@0: *dest++ = FioReadByte(); truelight@0: return; truelight@0: } truelight@0: truelight@0: while (num) { truelight@0: i = FioReadByte(); truelight@0: if (i>=0) { truelight@0: num -= i; truelight@0: while (i--) truelight@0: *dest++ = FioReadByte(); truelight@0: } else { truelight@0: dist = -(((i&7)<<8)|FioReadByte()); truelight@0: i = -(i >> 3); truelight@0: num -= i; truelight@184: truelight@0: rel = &dest[dist]; truelight@0: while (i--) truelight@0: *dest++ = *rel++; 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: darkvater@361: if ((size = FioReadWord()) == 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: darkvater@361: 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]--; darkvater@361: 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: dominik@37: static void SkipSprites(int count) dominik@37: { dominik@37: while(count>0) dominik@37: { dominik@37: uint16 size; dominik@37: if ( (size = FioReadWord()) == 0) dominik@37: return; dominik@37: dominik@37: ReadSpriteHeaderSkipData(size, NUM_SPRITES-1); dominik@37: count--; dominik@37: } dominik@37: } dominik@37: truelight@0: // Checks, if trg1r.grf is the Windows version truelight@0: static bool CheckGrfFile() truelight@0: { truelight@0: byte check; truelight@0: FioSeekToFile(38); // Byte 38 has the value 0x21 in Windows version, 0x07 in DOS truelight@0: check = FioReadWord(); truelight@0: FioSeekToFile(0); truelight@0: return (check==0x21); truelight@0: } truelight@0: 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: if(file_index==0 && !_ignore_wrong_grf) truelight@0: if(!CheckGrfFile()) truelight@0: error("Wrong version of grf files!\nThe Windows 95 edition of Transport Tycoon Deluxe is required to play OTTD!\n(you can disable this message by starting with the \"-i\" switch."); 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; darkvater@365: 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: darkvater@365: 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: truelight@0: for(;(start=*index_tbl++) != 0xffff;) { dominik@37: int end = *index_tbl++; dominik@37: 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 { dominik@37: bool b = LoadNextSprite(start, file_index); dominik@37: assert(b); dominik@37: } while (++start <= end); dominik@37: } truelight@0: } truelight@184: } truelight@0: truelight@0: 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_DATA(x) (*(uint32*)(x)) truelight@0: #define S_FREE_MASK 1 truelight@0: #define S_HDRSIZE sizeof(uint32) truelight@0: truelight@0: static uint32 GetSpriteCacheUsage() truelight@0: { truelight@0: byte *s = _spritecache_ptr; truelight@0: size_t cur_size, tot_size = 0; truelight@0: for(; (cur_size=S_DATA(s)) != 0; s+=cur_size) { truelight@0: if ( cur_size & S_FREE_MASK ) { truelight@0: cur_size--; truelight@0: } else { truelight@0: tot_size += cur_size; truelight@0: } truelight@0: } truelight@0: truelight@0: return tot_size; truelight@0: } truelight@0: truelight@0: truelight@0: void IncreaseSpriteLRU() 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: truelight@0: 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 truelight@0: 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. truelight@0: static void CompactSpriteCache() truelight@0: { truelight@0: byte *s, *t; truelight@0: size_t size, sizeb, cur_size; truelight@0: int i; truelight@184: truelight@0: DEBUG(spritecache, 2) ("compacting sprite cache, inuse=%d", GetSpriteCacheUsage()); truelight@0: truelight@0: s = _spritecache_ptr; truelight@184: truelight@0: while (true) { truelight@0: size = S_DATA(s); truelight@184: truelight@0: // Only look for free blocks. truelight@0: if (size & S_FREE_MASK) { truelight@0: size -= S_FREE_MASK; truelight@0: // Since free blocks are automatically coalesced, this should hold true. truelight@0: assert(!(S_DATA(s+size) & S_FREE_MASK)); truelight@184: truelight@0: // If the next block is the sentinel block, we can safely return truelight@0: if ( (sizeb=S_DATA(s + size)) == 0) truelight@0: break; truelight@0: truelight@0: // Locate the sprite number belonging to the next pointer. truelight@0: for(i=0,t=s+size+S_HDRSIZE; _sprite_ptr[i] != t; i++) {assert(i < NUM_SPRITES);} truelight@0: truelight@0: // If it's locked, we must not move it. truelight@0: #if defined(WANT_LOCKED) truelight@0: if (!_sprite_locked[i]) { truelight@0: #endif truelight@0: truelight@0: // Offset the sprite pointer by the size of the free block truelight@0: _sprite_ptr[i] -= size; truelight@0: truelight@184: // Move the memory tron@425: memmove(s + S_HDRSIZE, s + S_HDRSIZE + size, sizeb - S_HDRSIZE); truelight@0: truelight@0: // What we just did had the effect of swapping the allocated block with the free block, so we need to update truelight@0: // the block pointers. First update the allocated one. It is in use. truelight@0: S_DATA(s) = sizeb; truelight@184: truelight@0: // Then coalesce the free ones that follow. truelight@0: s += sizeb; truelight@0: while ((cur_size = S_DATA(s+size)) & S_FREE_MASK) truelight@0: size += cur_size - S_FREE_MASK; truelight@0: S_DATA(s) = size + S_FREE_MASK; truelight@0: continue; truelight@0: #if defined(WANT_LOCKED) truelight@0: } truelight@0: #endif truelight@0: } truelight@0: // Continue with next block until the sentinel is reached. truelight@0: s += size; truelight@0: if (size == 0) truelight@0: break; truelight@0: } truelight@0: } truelight@0: truelight@0: static void DeleteEntryFromSpriteCache() truelight@0: { truelight@0: int i; truelight@0: int best = -1; truelight@0: byte *s; truelight@0: size_t cur_size, cur; 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; truelight@0: 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) truelight@0: && !_sprite_locked[i]) { truelight@0: #else truelight@0: ) { truelight@0: #endif 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; truelight@0: 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) truelight@0: s = _sprite_ptr[best]; truelight@0: assert(!(S_DATA(s - S_HDRSIZE) & S_FREE_MASK)); truelight@0: S_DATA(s - S_HDRSIZE) += S_FREE_MASK; truelight@0: _sprite_ptr[best] = NULL; truelight@0: truelight@0: // And coalesce adjacent free blocks truelight@0: s = _spritecache_ptr; truelight@0: for(; (cur_size=S_DATA(s)) != 0; s+=cur_size) { truelight@0: if ( cur_size & S_FREE_MASK ) { truelight@0: while ((cur=S_DATA(s+cur_size-S_FREE_MASK)) & S_FREE_MASK) { truelight@0: cur_size += cur - S_FREE_MASK; truelight@0: S_DATA(s) = cur_size; truelight@0: } truelight@0: cur_size--; truelight@0: } truelight@0: } truelight@0: } truelight@0: truelight@0: static byte *LoadSpriteToMem(int sprite) truelight@0: { truelight@0: byte *s; truelight@0: size_t mem_req, cur_size; truelight@0: truelight@0: DEBUG(spritecache, 9) ("load sprite %d", sprite); truelight@0: truelight@0: restart: truelight@0: // Number of needed bytes truelight@0: mem_req = _sprite_size[sprite] + S_HDRSIZE; truelight@0: truelight@0: // Align this to an uint32 boundary. This also makes sure that the 2 least bit are not used, truelight@0: // so we could use those for other things. truelight@0: mem_req = (mem_req + sizeof(uint32) - 1) & ~(sizeof(uint32) - 1); truelight@0: truelight@0: s = _spritecache_ptr; truelight@0: for(;;) { truelight@0: for(;;) { truelight@0: cur_size = S_DATA(s); truelight@0: if (! (cur_size & S_FREE_MASK) ) break; truelight@0: truelight@0: cur_size -= S_FREE_MASK; truelight@0: truelight@0: // Now s points at a free block. truelight@0: // The block is exactly the size we need? truelight@0: if (cur_size != mem_req) { truelight@184: truelight@0: // No.. is it too small? truelight@0: if (cur_size < mem_req + S_HDRSIZE) truelight@0: break; truelight@0: truelight@0: // Block was big enough, and we need to inject a free block too. truelight@0: S_DATA(s + mem_req) = cur_size - mem_req + S_FREE_MASK; truelight@0: truelight@0: } truelight@0: // Set size and in use truelight@0: S_DATA(s) = mem_req; truelight@0: truelight@0: _sprite_ptr[sprite] = (s += S_HDRSIZE); truelight@0: truelight@0: FioSeekToFile(_sprite_file_pos[sprite]); truelight@0: ReadSprite(_sprite_size[sprite], s); truelight@184: truelight@0: // Patch the height to compensate for a TTD bug? truelight@0: if (sprite == 142) { s[1] = 10; } truelight@0: truelight@0: // Return sprite ptr truelight@0: return s; truelight@0: } truelight@0: truelight@0: // Reached sentinel, but no block found yet. Need to delete some old entries. truelight@0: if (cur_size == 0) { truelight@0: DeleteEntryFromSpriteCache(); truelight@0: goto restart; truelight@0: } truelight@184: truelight@0: s += cur_size; 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: truelight@184: byte *GetSpritePtr(uint 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: dominik@143: #define OPENTTD_SPRITES_COUNT 70 truelight@0: static const uint16 _openttd_grf_indexes[] = { truelight@0: SPR_OPENTTD_BASE+0, SPR_OPENTTD_BASE+7, // icons etc truelight@0: 98,98, // euro symbol medium size truelight@0: 546,546, // euro symbol large size truelight@0: SPR_OPENTTD_BASE+10, 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: truelight@0: static void LoadSpriteTables() truelight@0: { truelight@0: int i,j; 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: 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: truelight@0: for(i=0; _filename_list[i] != NULL; i++) { truelight@0: load_index += LoadGrfFile(_filename_list[i], 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) truelight@184: LoadGrfIndexed(_landscape_filenames[_sprite_page_to_load-1], _landscape_spriteindexes[_sprite_page_to_load-1], i++); truelight@184: dominik@37: LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++); truelight@0: 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: darkvater@368: /* Load newgrf sprites */ darkvater@368: darkvater@392: _custom_sprites_base = load_index; darkvater@392: darkvater@368: _loading_stage = 0; darkvater@368: for (j = 0; j != lengthof(_newgrf_files) && _newgrf_files[j]; j++) { darkvater@366: InitNewGRFFile(_newgrf_files[j], load_index); darkvater@365: load_index += LoadNewGrfFile(_newgrf_files[j], load_index, i++); darkvater@366: } truelight@0: darkvater@368: _loading_stage = 1; darkvater@392: load_index = _custom_sprites_base; darkvater@368: for (j = 0; j != lengthof(_newgrf_files) && _newgrf_files[j]; j++) darkvater@368: load_index += LoadNewGrfFile(_newgrf_files[j], load_index, i++); darkvater@368: darkvater@368: 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: darkvater@366: for(i = 0; _filename_list[i] != NULL; i++) truelight@184: FioOpenFile(i,_filename_list[i]); truelight@184: truelight@184: FioOpenFile(i++, "openttd.grf"); truelight@184: truelight@0: if (_sprite_page_to_load != 0) truelight@184: FioOpenFile(i++, _landscape_filenames[_sprite_page_to_load-1]); 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? truelight@184: for(j=0; j!=lengthof(_newgrf_files) && _newgrf_files[j]; j++) truelight@184: FioOpenFile(i++, _newgrf_files[j]); truelight@0: } truelight@0: truelight@0: _compact_cache_counter = 0; truelight@0: } truelight@0: truelight@0: void GfxInitSpriteMem(byte *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: // Sentinel block (identified by size=0) truelight@0: S_DATA(ptr + size - S_HDRSIZE) = 0; truelight@0: // A big free block truelight@0: S_DATA(ptr) = size - S_HDRSIZE + S_FREE_MASK; truelight@0: truelight@0: memset(_sprite_ptr, 0, sizeof(_sprite_ptr)); truelight@0: } truelight@0: truelight@0: truelight@0: void GfxLoadSprites() { 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: truelight@0: const SpriteDimension *GetSpriteDimension(uint sprite) truelight@0: { truelight@0: static SpriteDimension sd_static; truelight@0: SpriteDimension *sd; truelight@0: truelight@0: #ifndef WANT_SPRITESIZES truelight@0: byte *p; truelight@0: truelight@0: p = _sprite_ptr[sprite]; truelight@0: if (p == NULL) truelight@0: p = GetSpritePtr(sprite); truelight@0: truelight@0: /* decode sprite header */ truelight@0: sd = &sd_static; truelight@0: sd->xoffs = (int16)READ_LE_UINT16(&((SpriteHdr*)p)->x_offs); truelight@0: sd->yoffs = (int16)READ_LE_UINT16(&((SpriteHdr*)p)->y_offs); truelight@0: sd->xsize = READ_LE_UINT16(&((SpriteHdr*)p)->width); truelight@0: sd->ysize = ((SpriteHdr*)p)->height; truelight@0: #else truelight@0: sd = &sd_static; truelight@0: sd->xoffs = _sprite_xoffs[sprite]; truelight@0: sd->yoffs = _sprite_yoffs[sprite]; truelight@0: sd->xsize = _sprite_xsize[sprite]; truelight@0: sd->ysize = _sprite_ysize[sprite]; truelight@0: #endif truelight@0: /* sd->xoffs = _sprite_xoffs[sprite]; truelight@0: sd->yoffs = _sprite_yoffs[sprite]; truelight@0: sd->xsize = _sprite_xsize[sprite]; truelight@0: sd->ysize = _sprite_ysize[sprite]; truelight@0: */ truelight@0: return sd; truelight@0: } truelight@184: